libgo: update to go1.15rc1

Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/245157
This commit is contained in:
Ian Lance Taylor 2020-07-27 22:27:54 -07:00
parent 75a23e5903
commit f75af8c146
916 changed files with 45830 additions and 14928 deletions

View file

@ -1,4 +1,4 @@
63bc2430187efe5ff47e9c7b9cd6d40b350ee7d7
2c390ba951e83b547f6387cc9e19436c085b3775
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.

View file

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

View file

@ -391,6 +391,11 @@ toolexeclibgotexttemplatedir = $(toolexeclibgotextdir)/template
toolexeclibgotexttemplate_DATA = \
text/template/parse.gox
toolexeclibgotimedir = $(toolexeclibgodir)/time
toolexeclibgotime_DATA = \
time/tzdata.gox
toolexeclibgounicodedir = $(toolexeclibgodir)/unicode
toolexeclibgounicode_DATA = \
@ -400,7 +405,8 @@ toolexeclibgounicode_DATA = \
# Some internal packages are needed to bootstrap the gc toolchain.
toolexeclibgointernaldir = $(toolexeclibgodir)/internal
toolexeclibgointernal_DATA = \
internal/reflectlite.gox
internal/reflectlite.gox \
internal/unsafeheader.gox
# Some packages are only needed for tests, so unlike the other
# internal packages nothing will explicitly depend on them.
@ -409,11 +415,11 @@ noinst_DATA = \
golang.org/x/net/nettest.gox \
internal/cfg.gox \
internal/obscuretestdata.gox \
internal/profile.gox \
internal/testenv.gox \
internal/trace.gox \
net/internal/socktest.gox \
os/signal/internal/pty.gox \
runtime/pprof/internal/profile.gox
os/signal/internal/pty.gox
if LIBGO_IS_RTEMS
rtems_task_variable_add_file = runtime/rtems-task-variable-add.c
@ -706,9 +712,9 @@ syscall_lib_clone_lo =
endif
if LIBGO_IS_X86
golangorg_x_sys_cpu_gccgo_lo = golang.org/x/sys/cpu_gccgo.lo
golangorg_x_sys_cpu_gccgo_x86_lo = golang.org/x/sys/cpu_gccgo_x86.lo
else
golangorg_x_sys_cpu_gccgo_lo =
golangorg_x_sys_cpu_gccgo_x86_lo =
endif
PACKAGES = $(shell cat $(srcdir)/libgo-packages.txt)
@ -728,7 +734,7 @@ libgo_go_objs = \
runtime/internal/atomic_c.lo \
sync/atomic_c.lo \
internal/cpu/cpu_gccgo.lo \
$(golangorg_x_sys_cpu_gccgo_lo)
$(golangorg_x_sys_cpu_gccgo_x86_lo)
libgo_ldflags = \
-version-info $(libtool_VERSION) $(PTHREAD_CFLAGS) $(AM_LDFLAGS)
@ -1008,6 +1014,7 @@ 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_test = $(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
@ -1060,9 +1067,9 @@ internal/cpu/cpu_gccgo.lo: go/internal/cpu/cpu_gccgo.c runtime.inc
$(LTCOMPILE) -c -o $@ $(srcdir)/go/internal/cpu/cpu_gccgo.c
# Similarly, golang.org/x/sys/cpu needs some C code.
golang.org/x/sys/cpu_gccgo.lo: go/golang.org/x/sys/cpu/cpu_gccgo.c runtime.inc
golang.org/x/sys/cpu_gccgo_x86.lo: go/golang.org/x/sys/cpu/cpu_gccgo_x86.c runtime.inc
@$(MKDIR_P) golang.org/x/sys
$(LTCOMPILE) -c -o $@ $(srcdir)/go/golang.org/x/sys/cpu/cpu_gccgo.c
$(LTCOMPILE) -c -o $@ $(srcdir)/go/golang.org/x/sys/cpu/cpu_gccgo_x86.c
# Solaris 11.4 changed the type of fields in struct stat.
# Use a build tag, based on a configure check, to cope.
@ -1238,7 +1245,7 @@ all-local: $(ALL_LOCAL_DEPS)
MAJOR=$(firstword $(subst :, ,$(libtool_VERSION)))
add-aix-fat-library: all-multi
@if test "$(MULTIBUILDTOP)" = ""; then \
${AR} -X$(AIX_DEFAULT_ARCH) rc .libs/$(PACKAGE).a ../ppc$(AIX_DEFAULT_ARCH)/$(PACKAGE)/.libs/$(PACKAGE).so.$(MAJOR); \
${AR} -X$(AIX_DEFAULT_ARCH) rc ../pthread/$(PACKAGE)/.libs/$(PACKAGE).a ../pthread/ppc$(AIX_DEFAULT_ARCH)/$(PACKAGE)/.libs/$(PACKAGE).so.$(MAJOR); \
fi
@if test "$(MULTIBUILDTOP)" = ""; then \
${AR} -X$(AIX_DEFAULT_ARCH) rc .libs/$(PACKAGE).a ../ppc$(AIX_DEFAULT_ARCH)/$(PACKAGE)/.libs/$(PACKAGE).so.$(MAJOR); \
${AR} -X$(AIX_DEFAULT_ARCH) rc ../pthread/$(PACKAGE)/.libs/$(PACKAGE).a ../pthread/ppc$(AIX_DEFAULT_ARCH)/$(PACKAGE)/.libs/$(PACKAGE).so.$(MAJOR); \
fi

View file

@ -191,6 +191,7 @@ am__installdirs = "$(DESTDIR)$(toolexeclibdir)" \
"$(DESTDIR)$(toolexeclibgotestinginternaldir)" \
"$(DESTDIR)$(toolexeclibgotextdir)" \
"$(DESTDIR)$(toolexeclibgotexttemplatedir)" \
"$(DESTDIR)$(toolexeclibgotimedir)" \
"$(DESTDIR)$(toolexeclibgounicodedir)"
LIBRARIES = $(noinst_LIBRARIES) $(toolexeclib_LIBRARIES)
ARFLAGS = cru
@ -218,7 +219,8 @@ am_libgotool_a_OBJECTS =
libgotool_a_OBJECTS = $(am_libgotool_a_OBJECTS)
LTLIBRARIES = $(toolexeclib_LTLIBRARIES)
@LIBGO_IS_LINUX_TRUE@am__DEPENDENCIES_1 = syscall/clone_linux.lo
@LIBGO_IS_X86_TRUE@am__DEPENDENCIES_2 = golang.org/x/sys/cpu_gccgo.lo
@LIBGO_IS_X86_TRUE@am__DEPENDENCIES_2 = \
@LIBGO_IS_X86_TRUE@ golang.org/x/sys/cpu_gccgo_x86.lo
am__DEPENDENCIES_3 = $(addsuffix .lo,$(PACKAGES)) \
internal/bytealg/bytealg.lo reflect/makefunc_ffi_c.lo \
$(am__DEPENDENCIES_1) syscall/errno.lo syscall/signame.lo \
@ -345,7 +347,8 @@ DATA = $(noinst_DATA) $(toolexeclibgo_DATA) \
$(toolexeclibgoruntime_DATA) $(toolexeclibgosync_DATA) \
$(toolexeclibgotesting_DATA) \
$(toolexeclibgotestinginternal_DATA) $(toolexeclibgotext_DATA) \
$(toolexeclibgotexttemplate_DATA) $(toolexeclibgounicode_DATA)
$(toolexeclibgotexttemplate_DATA) $(toolexeclibgotime_DATA) \
$(toolexeclibgounicode_DATA)
RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
distclean-recursive maintainer-clean-recursive
am__recursive_targets = \
@ -856,6 +859,10 @@ toolexeclibgotexttemplatedir = $(toolexeclibgotextdir)/template
toolexeclibgotexttemplate_DATA = \
text/template/parse.gox
toolexeclibgotimedir = $(toolexeclibgodir)/time
toolexeclibgotime_DATA = \
time/tzdata.gox
toolexeclibgounicodedir = $(toolexeclibgodir)/unicode
toolexeclibgounicode_DATA = \
unicode/utf16.gox \
@ -865,16 +872,17 @@ toolexeclibgounicode_DATA = \
# Some internal packages are needed to bootstrap the gc toolchain.
toolexeclibgointernaldir = $(toolexeclibgodir)/internal
toolexeclibgointernal_DATA = \
internal/reflectlite.gox
internal/reflectlite.gox \
internal/unsafeheader.gox
# Some packages are only needed for tests, so unlike the other
# internal packages nothing will explicitly depend on them.
# Force them to be built.
noinst_DATA = golang.org/x/net/nettest.gox internal/cfg.gox \
internal/obscuretestdata.gox internal/testenv.gox \
internal/trace.gox net/internal/socktest.gox \
os/signal/internal/pty.gox runtime/pprof/internal/profile.gox \
internal/obscuretestdata.gox internal/profile.gox \
internal/testenv.gox internal/trace.gox \
net/internal/socktest.gox os/signal/internal/pty.gox \
zdefaultcc.go
@LIBGO_IS_RTEMS_FALSE@rtems_task_variable_add_file =
@LIBGO_IS_RTEMS_TRUE@rtems_task_variable_add_file = runtime/rtems-task-variable-add.c
@ -923,8 +931,8 @@ SYSINFO_FLAGS = \
@LIBGO_IS_LINUX_FALSE@syscall_lib_clone_lo =
@LIBGO_IS_LINUX_TRUE@syscall_lib_clone_lo = syscall/clone_linux.lo
@LIBGO_IS_X86_FALSE@golangorg_x_sys_cpu_gccgo_lo =
@LIBGO_IS_X86_TRUE@golangorg_x_sys_cpu_gccgo_lo = golang.org/x/sys/cpu_gccgo.lo
@LIBGO_IS_X86_FALSE@golangorg_x_sys_cpu_gccgo_x86_lo =
@LIBGO_IS_X86_TRUE@golangorg_x_sys_cpu_gccgo_x86_lo = golang.org/x/sys/cpu_gccgo_x86.lo
PACKAGES = $(shell cat $(srcdir)/libgo-packages.txt)
libgo_go_objs = \
$(addsuffix .lo,$(PACKAGES)) \
@ -941,7 +949,7 @@ libgo_go_objs = \
runtime/internal/atomic_c.lo \
sync/atomic_c.lo \
internal/cpu/cpu_gccgo.lo \
$(golangorg_x_sys_cpu_gccgo_lo)
$(golangorg_x_sys_cpu_gccgo_x86_lo)
libgo_ldflags = \
-version-info $(libtool_VERSION) $(PTHREAD_CFLAGS) $(AM_LDFLAGS)
@ -1130,6 +1138,7 @@ 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_test = $(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
@ -2223,6 +2232,27 @@ uninstall-toolexeclibgotexttemplateDATA:
@list='$(toolexeclibgotexttemplate_DATA)'; test -n "$(toolexeclibgotexttemplatedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
dir='$(DESTDIR)$(toolexeclibgotexttemplatedir)'; $(am__uninstall_files_from_dir)
install-toolexeclibgotimeDATA: $(toolexeclibgotime_DATA)
@$(NORMAL_INSTALL)
@list='$(toolexeclibgotime_DATA)'; test -n "$(toolexeclibgotimedir)" || list=; \
if test -n "$$list"; then \
echo " $(MKDIR_P) '$(DESTDIR)$(toolexeclibgotimedir)'"; \
$(MKDIR_P) "$(DESTDIR)$(toolexeclibgotimedir)" || exit 1; \
fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
done | $(am__base_list) | \
while read files; do \
echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(toolexeclibgotimedir)'"; \
$(INSTALL_DATA) $$files "$(DESTDIR)$(toolexeclibgotimedir)" || exit $$?; \
done
uninstall-toolexeclibgotimeDATA:
@$(NORMAL_UNINSTALL)
@list='$(toolexeclibgotime_DATA)'; test -n "$(toolexeclibgotimedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
dir='$(DESTDIR)$(toolexeclibgotimedir)'; $(am__uninstall_files_from_dir)
install-toolexeclibgounicodeDATA: $(toolexeclibgounicode_DATA)
@$(NORMAL_INSTALL)
@list='$(toolexeclibgounicode_DATA)'; test -n "$(toolexeclibgounicodedir)" || list=; \
@ -2356,7 +2386,7 @@ all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) $(DATA) config.h \
all-local
installdirs: installdirs-recursive
installdirs-am:
for dir in "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibgodir)" "$(DESTDIR)$(toolexeclibgoarchivedir)" "$(DESTDIR)$(toolexeclibgocompressdir)" "$(DESTDIR)$(toolexeclibgocontainerdir)" "$(DESTDIR)$(toolexeclibgocryptodir)" "$(DESTDIR)$(toolexeclibgocryptox509dir)" "$(DESTDIR)$(toolexeclibgodatabasedir)" "$(DESTDIR)$(toolexeclibgodatabasesqldir)" "$(DESTDIR)$(toolexeclibgodebugdir)" "$(DESTDIR)$(toolexeclibgoencodingdir)" "$(DESTDIR)$(toolexeclibgogodir)" "$(DESTDIR)$(toolexeclibgohashdir)" "$(DESTDIR)$(toolexeclibgohtmldir)" "$(DESTDIR)$(toolexeclibgoimagedir)" "$(DESTDIR)$(toolexeclibgoimagecolordir)" "$(DESTDIR)$(toolexeclibgoindexdir)" "$(DESTDIR)$(toolexeclibgointernaldir)" "$(DESTDIR)$(toolexeclibgoiodir)" "$(DESTDIR)$(toolexeclibgologdir)" "$(DESTDIR)$(toolexeclibgomathdir)" "$(DESTDIR)$(toolexeclibgomimedir)" "$(DESTDIR)$(toolexeclibgonetdir)" "$(DESTDIR)$(toolexeclibgonethttpdir)" "$(DESTDIR)$(toolexeclibgonetrpcdir)" "$(DESTDIR)$(toolexeclibgoosdir)" "$(DESTDIR)$(toolexeclibgopathdir)" "$(DESTDIR)$(toolexeclibgoregexpdir)" "$(DESTDIR)$(toolexeclibgoruntimedir)" "$(DESTDIR)$(toolexeclibgosyncdir)" "$(DESTDIR)$(toolexeclibgotestingdir)" "$(DESTDIR)$(toolexeclibgotestinginternaldir)" "$(DESTDIR)$(toolexeclibgotextdir)" "$(DESTDIR)$(toolexeclibgotexttemplatedir)" "$(DESTDIR)$(toolexeclibgounicodedir)"; do \
for dir in "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibgodir)" "$(DESTDIR)$(toolexeclibgoarchivedir)" "$(DESTDIR)$(toolexeclibgocompressdir)" "$(DESTDIR)$(toolexeclibgocontainerdir)" "$(DESTDIR)$(toolexeclibgocryptodir)" "$(DESTDIR)$(toolexeclibgocryptox509dir)" "$(DESTDIR)$(toolexeclibgodatabasedir)" "$(DESTDIR)$(toolexeclibgodatabasesqldir)" "$(DESTDIR)$(toolexeclibgodebugdir)" "$(DESTDIR)$(toolexeclibgoencodingdir)" "$(DESTDIR)$(toolexeclibgogodir)" "$(DESTDIR)$(toolexeclibgohashdir)" "$(DESTDIR)$(toolexeclibgohtmldir)" "$(DESTDIR)$(toolexeclibgoimagedir)" "$(DESTDIR)$(toolexeclibgoimagecolordir)" "$(DESTDIR)$(toolexeclibgoindexdir)" "$(DESTDIR)$(toolexeclibgointernaldir)" "$(DESTDIR)$(toolexeclibgoiodir)" "$(DESTDIR)$(toolexeclibgologdir)" "$(DESTDIR)$(toolexeclibgomathdir)" "$(DESTDIR)$(toolexeclibgomimedir)" "$(DESTDIR)$(toolexeclibgonetdir)" "$(DESTDIR)$(toolexeclibgonethttpdir)" "$(DESTDIR)$(toolexeclibgonetrpcdir)" "$(DESTDIR)$(toolexeclibgoosdir)" "$(DESTDIR)$(toolexeclibgopathdir)" "$(DESTDIR)$(toolexeclibgoregexpdir)" "$(DESTDIR)$(toolexeclibgoruntimedir)" "$(DESTDIR)$(toolexeclibgosyncdir)" "$(DESTDIR)$(toolexeclibgotestingdir)" "$(DESTDIR)$(toolexeclibgotestinginternaldir)" "$(DESTDIR)$(toolexeclibgotextdir)" "$(DESTDIR)$(toolexeclibgotexttemplatedir)" "$(DESTDIR)$(toolexeclibgotimedir)" "$(DESTDIR)$(toolexeclibgounicodedir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-recursive
@ -2450,7 +2480,7 @@ install-exec-am: install-exec-local install-toolexeclibLIBRARIES \
install-toolexeclibgotestinginternalDATA \
install-toolexeclibgotextDATA \
install-toolexeclibgotexttemplateDATA \
install-toolexeclibgounicodeDATA
install-toolexeclibgotimeDATA install-toolexeclibgounicodeDATA
install-html: install-html-recursive
@ -2523,6 +2553,7 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
uninstall-toolexeclibgotestinginternalDATA \
uninstall-toolexeclibgotextDATA \
uninstall-toolexeclibgotexttemplateDATA \
uninstall-toolexeclibgotimeDATA \
uninstall-toolexeclibgounicodeDATA
.MAKE: $(am__recursive_targets) all install-am install-strip
@ -2564,12 +2595,13 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
install-toolexeclibgotestinginternalDATA \
install-toolexeclibgotextDATA \
install-toolexeclibgotexttemplateDATA \
install-toolexeclibgounicodeDATA installcheck installcheck-am \
installdirs installdirs-am maintainer-clean \
maintainer-clean-generic maintainer-clean-local mostlyclean \
mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
mostlyclean-local pdf pdf-am ps ps-am tags tags-am uninstall \
uninstall-am uninstall-toolexeclibLIBRARIES \
install-toolexeclibgotimeDATA install-toolexeclibgounicodeDATA \
installcheck installcheck-am installdirs installdirs-am \
maintainer-clean maintainer-clean-generic \
maintainer-clean-local mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool mostlyclean-local pdf \
pdf-am ps ps-am tags tags-am uninstall uninstall-am \
uninstall-toolexeclibLIBRARIES \
uninstall-toolexeclibLTLIBRARIES uninstall-toolexeclibgoDATA \
uninstall-toolexeclibgoarchiveDATA \
uninstall-toolexeclibgocompressDATA \
@ -2599,6 +2631,7 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
uninstall-toolexeclibgotestinginternalDATA \
uninstall-toolexeclibgotextDATA \
uninstall-toolexeclibgotexttemplateDATA \
uninstall-toolexeclibgotimeDATA \
uninstall-toolexeclibgounicodeDATA
.PRECIOUS: Makefile
@ -2929,9 +2962,9 @@ internal/cpu/cpu_gccgo.lo: go/internal/cpu/cpu_gccgo.c runtime.inc
$(LTCOMPILE) -c -o $@ $(srcdir)/go/internal/cpu/cpu_gccgo.c
# Similarly, golang.org/x/sys/cpu needs some C code.
golang.org/x/sys/cpu_gccgo.lo: go/golang.org/x/sys/cpu/cpu_gccgo.c runtime.inc
golang.org/x/sys/cpu_gccgo_x86.lo: go/golang.org/x/sys/cpu/cpu_gccgo_x86.c runtime.inc
@$(MKDIR_P) golang.org/x/sys
$(LTCOMPILE) -c -o $@ $(srcdir)/go/golang.org/x/sys/cpu/cpu_gccgo.c
$(LTCOMPILE) -c -o $@ $(srcdir)/go/golang.org/x/sys/cpu/cpu_gccgo_x86.c
# Build golang.org/x/net/route only on BSD systems.
@ -3087,8 +3120,8 @@ maintainer-clean-local: maintainer-clean-multi
all-local: $(ALL_LOCAL_DEPS)
add-aix-fat-library: all-multi
@if test "$(MULTIBUILDTOP)" = ""; then \
${AR} -X$(AIX_DEFAULT_ARCH) rc .libs/$(PACKAGE).a ../ppc$(AIX_DEFAULT_ARCH)/$(PACKAGE)/.libs/$(PACKAGE).so.$(MAJOR); \
${AR} -X$(AIX_DEFAULT_ARCH) rc ../pthread/$(PACKAGE)/.libs/$(PACKAGE).a ../pthread/ppc$(AIX_DEFAULT_ARCH)/$(PACKAGE)/.libs/$(PACKAGE).so.$(MAJOR); \
${AR} -X$(AIX_DEFAULT_ARCH) rc .libs/$(PACKAGE).a ../ppc$(AIX_DEFAULT_ARCH)/$(PACKAGE)/.libs/$(PACKAGE).so.$(MAJOR); \
${AR} -X$(AIX_DEFAULT_ARCH) rc ../pthread/$(PACKAGE)/.libs/$(PACKAGE).a ../pthread/ppc$(AIX_DEFAULT_ARCH)/$(PACKAGE)/.libs/$(PACKAGE).so.$(MAJOR); \
fi
# Tell versions [3.59,3.63) of GNU make to not export all variables.

View file

@ -1 +1 @@
go1.14.6
go1.15rc1

View file

@ -16,6 +16,7 @@ cmd/go/internal/modload
cmd/go/internal/mvs
cmd/go/internal/par
cmd/go/internal/search
cmd/go/internal/test
cmd/go/internal/txtar
cmd/go/internal/work
cmd/internal/buildid
@ -104,9 +105,11 @@ index/suffixarray
internal/cpu
internal/fmtsort
internal/poll
internal/profile
internal/reflectlite
internal/singleflight
internal/trace
internal/unsafeheader
internal/xcoff
io
io/ioutil
@ -152,7 +155,6 @@ runtime/internal/atomic
runtime/internal/math
runtime/internal/sys
runtime/pprof
runtime/pprof/internal/profile
runtime/trace
sort
strconv

6
libgo/configure vendored
View file

@ -2551,7 +2551,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
ac_config_headers="$ac_config_headers config.h"
libtool_VERSION=16:0:0
libtool_VERSION=17:0:0
# Default to --enable-multilib
@ -11501,7 +11501,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 11602 "configure"
#line 11504 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@ -11607,7 +11607,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 11708 "configure"
#line 11610 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H

View file

@ -10,7 +10,7 @@ AC_INIT(package-unused, version-unused,, libgo)
AC_CONFIG_SRCDIR(Makefile.am)
AC_CONFIG_HEADER(config.h)
libtool_VERSION=16:0:0
libtool_VERSION=17:0:0
AC_SUBST(libtool_VERSION)
AM_ENABLE_MULTILIB(, ..)

View file

@ -11,6 +11,7 @@ import (
"bytes"
"errors"
"io"
"strings"
"unicode/utf8"
)
@ -419,20 +420,16 @@ func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) {
return
}
// ReadBytes reads until the first occurrence of delim in the input,
// returning a slice containing the data up to and including the delimiter.
// If ReadBytes encounters an error before finding a delimiter,
// it returns the data read before the error and the error itself (often io.EOF).
// ReadBytes returns err != nil if and only if the returned data does not end in
// delim.
// For simple uses, a Scanner may be more convenient.
func (b *Reader) ReadBytes(delim byte) ([]byte, error) {
// Use ReadSlice to look for array,
// accumulating full buffers.
// collectFragments reads until the first occurrence of delim in the input. It
// returns (slice of full buffers, remaining bytes before delim, total number
// of bytes in the combined first two elements, error).
// The complete result is equal to
// `bytes.Join(append(fullBuffers, finalFragment), nil)`, which has a
// length of `totalLen`. The result is strucured in this way to allow callers
// to minimize allocations and copies.
func (b *Reader) collectFragments(delim byte) (fullBuffers [][]byte, finalFragment []byte, totalLen int, err error) {
var frag []byte
var full [][]byte
var err error
n := 0
// Use ReadSlice to look for delim, accumulating full buffers.
for {
var e error
frag, e = b.ReadSlice(delim)
@ -447,12 +444,23 @@ func (b *Reader) ReadBytes(delim byte) ([]byte, error) {
// Make a copy of the buffer.
buf := make([]byte, len(frag))
copy(buf, frag)
full = append(full, buf)
n += len(buf)
fullBuffers = append(fullBuffers, buf)
totalLen += len(buf)
}
n += len(frag)
totalLen += len(frag)
return fullBuffers, frag, totalLen, err
}
// ReadBytes reads until the first occurrence of delim in the input,
// returning a slice containing the data up to and including the delimiter.
// If ReadBytes encounters an error before finding a delimiter,
// it returns the data read before the error and the error itself (often io.EOF).
// ReadBytes returns err != nil if and only if the returned data does not end in
// delim.
// For simple uses, a Scanner may be more convenient.
func (b *Reader) ReadBytes(delim byte) ([]byte, error) {
full, frag, n, err := b.collectFragments(delim)
// Allocate new buffer to hold the full pieces and the fragment.
buf := make([]byte, n)
n = 0
@ -472,8 +480,16 @@ func (b *Reader) ReadBytes(delim byte) ([]byte, error) {
// delim.
// For simple uses, a Scanner may be more convenient.
func (b *Reader) ReadString(delim byte) (string, error) {
bytes, err := b.ReadBytes(delim)
return string(bytes), err
full, frag, n, err := b.collectFragments(delim)
// Allocate new buffer to hold the full pieces and the fragment.
var buf strings.Builder
buf.Grow(n)
// Copy full pieces and fragment in.
for _, fb := range full {
buf.Write(fb)
}
buf.Write(frag)
return buf.String(), err
}
// WriteTo implements io.WriterTo.

View file

@ -147,7 +147,7 @@ func TestReader(t *testing.T) {
for i := 0; i < len(texts)-1; i++ {
texts[i] = str + "\n"
all += texts[i]
str += string(i%26 + 'a')
str += string(rune(i)%26 + 'a')
}
texts[len(texts)-1] = all
@ -535,6 +535,23 @@ func TestReadWriteRune(t *testing.T) {
}
}
func TestReadStringAllocs(t *testing.T) {
r := strings.NewReader(" foo foo 42 42 42 42 42 42 42 42 4.2 4.2 4.2 4.2\n")
buf := NewReader(r)
allocs := testing.AllocsPerRun(100, func() {
r.Seek(0, io.SeekStart)
buf.Reset(r)
_, err := buf.ReadString('\n')
if err != nil {
t.Fatal(err)
}
})
if allocs != 1 {
t.Errorf("Unexpected number of allocations, got %f, want 1", allocs)
}
}
func TestWriter(t *testing.T) {
var data [8192]byte
@ -1644,6 +1661,21 @@ func BenchmarkReaderWriteToOptimal(b *testing.B) {
}
}
func BenchmarkReaderReadString(b *testing.B) {
r := strings.NewReader(" foo foo 42 42 42 42 42 42 42 42 4.2 4.2 4.2 4.2\n")
buf := NewReader(r)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
r.Seek(0, io.SeekStart)
buf.Reset(r)
_, err := buf.ReadString('\n')
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkWriterCopyOptimal(b *testing.B) {
// Optimal case is where the underlying writer implements io.ReaderFrom
srcBuf := bytes.NewBuffer(make([]byte, 8192))

View file

@ -69,6 +69,7 @@ var (
ErrTooLong = errors.New("bufio.Scanner: token too long")
ErrNegativeAdvance = errors.New("bufio.Scanner: SplitFunc returns negative advance count")
ErrAdvanceTooFar = errors.New("bufio.Scanner: SplitFunc returns advance count beyond input")
ErrBadReadCount = errors.New("bufio.Scanner: Read returned impossible count")
)
const (
@ -211,6 +212,10 @@ func (s *Scanner) Scan() bool {
// be extra careful: Scanner is for safe, simple jobs.
for loop := 0; ; {
n, err := s.r.Read(s.buf[s.end:len(s.buf)])
if n < 0 || len(s.buf)-s.end < n {
s.setErr(ErrBadReadCount)
break
}
s.end += n
if err != nil {
s.setErr(err)

View file

@ -537,3 +537,60 @@ func TestHugeBuffer(t *testing.T) {
t.Fatal("after scan:", s.Err())
}
}
// negativeEOFReader returns an invalid -1 at the end, as though it
// were wrapping the read system call.
type negativeEOFReader int
func (r *negativeEOFReader) Read(p []byte) (int, error) {
if *r > 0 {
c := int(*r)
if c > len(p) {
c = len(p)
}
for i := 0; i < c; i++ {
p[i] = 'a'
}
p[c-1] = '\n'
*r -= negativeEOFReader(c)
return c, nil
}
return -1, io.EOF
}
// Test that the scanner doesn't panic and returns ErrBadReadCount
// on a reader that returns a negative count of bytes read (issue 38053).
func TestNegativeEOFReader(t *testing.T) {
r := negativeEOFReader(10)
scanner := NewScanner(&r)
c := 0
for scanner.Scan() {
c++
if c > 1 {
t.Error("read too many lines")
break
}
}
if got, want := scanner.Err(), ErrBadReadCount; got != want {
t.Errorf("scanner.Err: got %v, want %v", got, want)
}
}
// largeReader returns an invalid count that is larger than the number
// of bytes requested.
type largeReader struct{}
func (largeReader) Read(p []byte) (int, error) {
return len(p) + 1, nil
}
// Test that the scanner doesn't panic and returns ErrBadReadCount
// on a reader that returns an impossibly large count of bytes read (issue 38053).
func TestLargeReader(t *testing.T) {
scanner := NewScanner(largeReader{})
for scanner.Scan() {
}
if got, want := scanner.Err(), ErrBadReadCount; got != want {
t.Errorf("scanner.Err: got %v, want %v", got, want)
}
}

View file

@ -8,7 +8,6 @@ import (
. "bytes"
"io"
"math/rand"
"runtime"
"testing"
"unicode/utf8"
)
@ -495,20 +494,20 @@ func TestGrow(t *testing.T) {
x := []byte{'x'}
y := []byte{'y'}
tmp := make([]byte, 72)
for _, startLen := range []int{0, 100, 1000, 10000, 100000} {
xBytes := Repeat(x, startLen)
for _, growLen := range []int{0, 100, 1000, 10000, 100000} {
for _, growLen := range []int{0, 100, 1000, 10000, 100000} {
for _, startLen := range []int{0, 100, 1000, 10000, 100000} {
xBytes := Repeat(x, startLen)
buf := NewBuffer(xBytes)
// If we read, this affects buf.off, which is good to test.
readBytes, _ := buf.Read(tmp)
buf.Grow(growLen)
yBytes := Repeat(y, growLen)
allocs := testing.AllocsPerRun(100, func() {
buf.Grow(growLen)
buf.Write(yBytes)
})
// Check no allocation occurs in write, as long as we're single-threaded.
var m1, m2 runtime.MemStats
runtime.ReadMemStats(&m1)
buf.Write(yBytes)
runtime.ReadMemStats(&m2)
if runtime.GOMAXPROCS(-1) == 1 && m1.Mallocs != m2.Mallocs {
if allocs != 0 {
t.Errorf("allocation occurred during write")
}
// Check that buffer has correct data.

View file

@ -117,17 +117,17 @@ func LastIndex(s, sep []byte) int {
return -1
}
// Rabin-Karp search from the end of the string
hashss, pow := hashStrRev(sep)
hashss, pow := bytealg.HashStrRevBytes(sep)
last := len(s) - n
var h uint32
for i := len(s) - 1; i >= last; i-- {
h = h*primeRK + uint32(s[i])
h = h*bytealg.PrimeRK + uint32(s[i])
}
if h == hashss && Equal(s[last:], sep) {
return last
}
for i := last - 1; i >= 0; i-- {
h *= primeRK
h *= bytealg.PrimeRK
h += uint32(s[i])
h -= pow * uint32(s[i+n])
if h == hashss && Equal(s[i:i+n], sep) {
@ -183,6 +183,29 @@ func IndexAny(s []byte, chars string) int {
// Avoid scanning all of s.
return -1
}
if len(s) == 1 {
r := rune(s[0])
if r >= utf8.RuneSelf {
// search utf8.RuneError.
for _, r = range chars {
if r == utf8.RuneError {
return 0
}
}
return -1
}
if bytealg.IndexByteString(chars, s[0]) >= 0 {
return 0
}
return -1
}
if len(chars) == 1 {
r := rune(chars[0])
if r >= utf8.RuneSelf {
r = utf8.RuneError
}
return IndexRune(s, r)
}
if len(s) > 8 {
if as, isASCII := makeASCIISet(chars); isASCII {
for i, c := range s {
@ -197,14 +220,26 @@ func IndexAny(s []byte, chars string) int {
for i := 0; i < len(s); i += width {
r := rune(s[i])
if r < utf8.RuneSelf {
width = 1
} else {
r, width = utf8.DecodeRune(s[i:])
}
for _, ch := range chars {
if r == ch {
if bytealg.IndexByteString(chars, s[i]) >= 0 {
return i
}
width = 1
continue
}
r, width = utf8.DecodeRune(s[i:])
if r == utf8.RuneError {
for _, r = range chars {
if r == utf8.RuneError {
return i
}
}
continue
}
// r is 2 to 4 bytes. Using strings.Index is more reasonable, but as the bytes
// package should not import the strings package, use bytealg.IndexString
// instead. And this does not seem to lose much performance.
if chars == string(r) || bytealg.IndexString(chars, string(r)) >= 0 {
return i
}
}
return -1
@ -229,14 +264,60 @@ func LastIndexAny(s []byte, chars string) int {
return -1
}
}
for i := len(s); i > 0; {
r, size := utf8.DecodeLastRune(s[:i])
i -= size
for _, c := range chars {
if r == c {
if len(s) == 1 {
r := rune(s[0])
if r >= utf8.RuneSelf {
for _, r = range chars {
if r == utf8.RuneError {
return 0
}
}
return -1
}
if bytealg.IndexByteString(chars, s[0]) >= 0 {
return 0
}
return -1
}
if len(chars) == 1 {
cr := rune(chars[0])
if cr >= utf8.RuneSelf {
cr = utf8.RuneError
}
for i := len(s); i > 0; {
r, size := utf8.DecodeLastRune(s[:i])
i -= size
if r == cr {
return i
}
}
return -1
}
for i := len(s); i > 0; {
r := rune(s[i-1])
if r < utf8.RuneSelf {
if bytealg.IndexByteString(chars, s[i-1]) >= 0 {
return i - 1
}
i--
continue
}
r, size := utf8.DecodeLastRune(s[:i])
i -= size
if r == utf8.RuneError {
for _, r = range chars {
if r == utf8.RuneError {
return i
}
}
continue
}
// r is 2 to 4 bytes. Using strings.Index is more reasonable, but as the bytes
// package should not import the strings package, use bytealg.IndexString
// instead. And this does not seem to lose much performance.
if chars == string(r) || bytealg.IndexString(chars, string(r)) >= 0 {
return i
}
}
return -1
}
@ -364,8 +445,9 @@ func Fields(s []byte) [][]byte {
// It splits the slice s at each run of code points c satisfying f(c) and
// returns a slice of subslices of s. If all code points in s satisfy f(c), or
// len(s) == 0, an empty slice is returned.
// FieldsFunc makes no guarantees about the order in which it calls f(c).
// If f does not return consistent results for a given c, FieldsFunc may crash.
//
// FieldsFunc makes no guarantees about the order in which it calls f(c)
// and assumes that f always returns the same value for a given c.
func FieldsFunc(s []byte, f func(rune) bool) [][]byte {
// A span is used to record a slice of s of the form s[start:end].
// The start index is inclusive and the end index is exclusive.
@ -376,8 +458,10 @@ func FieldsFunc(s []byte, f func(rune) bool) [][]byte {
spans := make([]span, 0, 32)
// Find the field start and end indices.
wasField := false
fromIndex := 0
// Doing this in a separate pass (rather than slicing the string s
// and collecting the result substrings right away) is significantly
// more efficient, possibly due to cache effects.
start := -1 // valid span start if >= 0
for i := 0; i < len(s); {
size := 1
r := rune(s[i])
@ -385,22 +469,21 @@ func FieldsFunc(s []byte, f func(rune) bool) [][]byte {
r, size = utf8.DecodeRune(s[i:])
}
if f(r) {
if wasField {
spans = append(spans, span{start: fromIndex, end: i})
wasField = false
if start >= 0 {
spans = append(spans, span{start, i})
start = -1
}
} else {
if !wasField {
fromIndex = i
wasField = true
if start < 0 {
start = i
}
}
i += size
}
// Last field might end at EOF.
if wasField {
spans = append(spans, span{fromIndex, len(s)})
if start >= 0 {
spans = append(spans, span{start, len(s)})
}
// Create subslices from recorded field indices.
@ -1019,11 +1102,11 @@ func Index(s, sep []byte) int {
if s[i] != c0 {
// IndexByte is faster than bytealg.Index, so use it as long as
// we're not getting lots of false positives.
o := IndexByte(s[i:t], c0)
o := IndexByte(s[i+1:t], c0)
if o < 0 {
return -1
}
i += o
i += o + 1
}
if s[i+1] == c1 && Equal(s[i:i+n], sep) {
return i
@ -1048,11 +1131,11 @@ func Index(s, sep []byte) int {
t := len(s) - n + 1
for i < t {
if s[i] != c0 {
o := IndexByte(s[i:t], c0)
o := IndexByte(s[i+1:t], c0)
if o < 0 {
break
}
i += o
i += o + 1
}
if s[i+1] == c1 && Equal(s[i:i+n], sep) {
return i
@ -1068,7 +1151,7 @@ func Index(s, sep []byte) int {
// 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)
j := bytealg.IndexRabinKarpBytes(s[i:], sep)
if j < 0 {
return -1
}
@ -1077,63 +1160,3 @@ func Index(s, sep []byte) int {
}
return -1
}
func indexRabinKarp(s, sep []byte) int {
// Rabin-Karp search
hashsep, pow := hashStr(sep)
n := len(sep)
var h uint32
for i := 0; i < n; i++ {
h = h*primeRK + uint32(s[i])
}
if h == hashsep && Equal(s[:n], sep) {
return 0
}
for i := n; i < len(s); {
h *= primeRK
h += uint32(s[i])
h -= pow * uint32(s[i-n])
i++
if h == hashsep && Equal(s[i-n:i], sep) {
return i - n
}
}
return -1
}
// primeRK is the prime base used in Rabin-Karp algorithm.
const primeRK = 16777619
// hashStr returns the hash and the appropriate multiplicative
// factor for use in Rabin-Karp algorithm.
func hashStr(sep []byte) (uint32, uint32) {
hash := uint32(0)
for i := 0; i < len(sep); i++ {
hash = hash*primeRK + uint32(sep[i])
}
var pow, sq uint32 = 1, primeRK
for i := len(sep); i > 0; i >>= 1 {
if i&1 != 0 {
pow *= sq
}
sq *= sq
}
return hash, pow
}
// hashStrRev returns the hash of the reverse of sep and the
// appropriate multiplicative factor for use in Rabin-Karp algorithm.
func hashStrRev(sep []byte) (uint32, uint32) {
hash := uint32(0)
for i := len(sep) - 1; i >= 0; i-- {
hash = hash*primeRK + uint32(sep[i])
}
var pow, sq uint32 = 1, primeRK
for i := len(sep); i > 0; i >>= 1 {
if i&1 != 0 {
pow *= sq
}
sq *= sq
}
return hash, pow
}

View file

@ -142,9 +142,10 @@ var indexTests = []BinOpTest{
{"barfoobarfooyyyzzzyyyzzzyyyzzzyyyxxxzzzyyy", "x", 33},
{"foofyfoobarfoobar", "y", 4},
{"oooooooooooooooooooooo", "r", -1},
// test fallback to Rabin-Karp.
{"oxoxoxoxoxoxoxoxoxoxoxoy", "oy", 22},
{"oxoxoxoxoxoxoxoxoxoxoxox", "oy", -1},
// test fallback to Rabin-Karp.
{"000000000000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000001", 5},
}
var lastIndexTests = []BinOpTest{
@ -169,6 +170,7 @@ var indexAnyTests = []BinOpTest{
{"", "abc", -1},
{"a", "", -1},
{"a", "a", 0},
{"\x80", "\xffb", 0},
{"aaa", "a", 0},
{"abc", "xyz", -1},
{"abc", "xcz", 2},
@ -179,6 +181,7 @@ var indexAnyTests = []BinOpTest{
{dots + dots + dots, " ", -1},
{"012abcba210", "\xffb", 4},
{"012\x80bcb\x80210", "\xffb", 3},
{"0123456\xcf\x80abc", "\xcfb\x80", 10},
}
var lastIndexAnyTests = []BinOpTest{
@ -187,6 +190,7 @@ var lastIndexAnyTests = []BinOpTest{
{"", "abc", -1},
{"a", "", -1},
{"a", "a", 0},
{"\x80", "\xffb", 0},
{"aaa", "a", 2},
{"abc", "xyz", -1},
{"abc", "ab", 1},
@ -197,6 +201,7 @@ var lastIndexAnyTests = []BinOpTest{
{dots + dots + dots, " ", -1},
{"012abcba210", "\xffb", 6},
{"012\x80bcb\x80210", "\xffb", 7},
{"0123456\xcf\x80abc", "\xcfb\x80", 10},
}
// Execute f on each test case. funcName should be the name of f; it's used
@ -210,6 +215,27 @@ func runIndexTests(t *testing.T, f func(s, sep []byte) int, funcName string, tes
t.Errorf("%s(%q,%q) = %v; want %v", funcName, a, b, actual, test.i)
}
}
var allocTests = []struct {
a []byte
b []byte
i int
}{
// case for function Index.
{[]byte("000000000000000000000000000000000000000000000000000000000000000000000001"), []byte("0000000000000000000000000000000000000000000000000000000000000000001"), 5},
// case for function LastIndex.
{[]byte("000000000000000000000000000000000000000000000000000000000000000010000"), []byte("00000000000000000000000000000000000000000000000000000000000001"), 3},
}
allocs := testing.AllocsPerRun(100, func() {
if i := Index(allocTests[1].a, allocTests[1].b); i != allocTests[1].i {
t.Errorf("Index([]byte(%q), []byte(%q)) = %v; want %v", allocTests[1].a, allocTests[1].b, i, allocTests[1].i)
}
if i := LastIndex(allocTests[0].a, allocTests[0].b); i != allocTests[0].i {
t.Errorf("LastIndex([]byte(%q), []byte(%q)) = %v; want %v", allocTests[0].a, allocTests[0].b, i, allocTests[0].i)
}
})
if allocs != 0 {
t.Errorf("expected no allocations, got %f", allocs)
}
}
func runIndexAnyTests(t *testing.T, f func(s []byte, chars string) int, funcName string, testCases []BinOpTest) {
@ -1873,10 +1899,10 @@ func BenchmarkBytesCompare(b *testing.B) {
}
func BenchmarkIndexAnyASCII(b *testing.B) {
x := Repeat([]byte{'#'}, 4096) // Never matches set
cs := "0123456789abcdef"
for k := 1; k <= 4096; k <<= 4 {
for j := 1; j <= 16; j <<= 1 {
x := Repeat([]byte{'#'}, 2048) // Never matches set
cs := "0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz"
for k := 1; k <= 2048; k <<= 4 {
for j := 1; j <= 64; j <<= 1 {
b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) {
for i := 0; i < b.N; i++ {
IndexAny(x[:k], cs[:j])
@ -1886,6 +1912,48 @@ func BenchmarkIndexAnyASCII(b *testing.B) {
}
}
func BenchmarkIndexAnyUTF8(b *testing.B) {
x := Repeat([]byte{'#'}, 2048) // Never matches set
cs := "你好世界, hello world. 你好世界, hello world. 你好世界, hello world."
for k := 1; k <= 2048; k <<= 4 {
for j := 1; j <= 64; j <<= 1 {
b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) {
for i := 0; i < b.N; i++ {
IndexAny(x[:k], cs[:j])
}
})
}
}
}
func BenchmarkLastIndexAnyASCII(b *testing.B) {
x := Repeat([]byte{'#'}, 2048) // Never matches set
cs := "0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz"
for k := 1; k <= 2048; k <<= 4 {
for j := 1; j <= 64; j <<= 1 {
b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) {
for i := 0; i < b.N; i++ {
LastIndexAny(x[:k], cs[:j])
}
})
}
}
}
func BenchmarkLastIndexAnyUTF8(b *testing.B) {
x := Repeat([]byte{'#'}, 2048) // Never matches set
cs := "你好世界, hello world. 你好世界, hello world. 你好世界, hello world."
for k := 1; k <= 2048; k <<= 4 {
for j := 1; j <= 64; j <<= 1 {
b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) {
for i := 0; i < b.N; i++ {
LastIndexAny(x[:k], cs[:j])
}
})
}
}
}
func BenchmarkTrimASCII(b *testing.B) {
cs := "0123456789abcdef"
for k := 1; k <= 4096; k <<= 4 {

View file

@ -413,7 +413,7 @@ type in Go are instead represented by a uintptr. Those include:
jobjectArray
jweak
3. The EGLDisplay type from the EGL API.
3. The EGLDisplay and EGLConfig types from the EGL API.
These types are uintptr on the Go side because they would otherwise
confuse the Go garbage collector; they are sometimes not really
@ -429,11 +429,16 @@ from Go 1.9 and earlier, use the cftype or jni rewrites in the Go fix tool:
It will replace nil with 0 in the appropriate places.
The EGLDisplay case were introduced in Go 1.12. Use the egl rewrite
The EGLDisplay case was introduced in Go 1.12. Use the egl rewrite
to auto-update code from Go 1.11 and earlier:
go tool fix -r egl <pkg>
The EGLConfig case was introduced in Go 1.15. Use the eglconf rewrite
to auto-update code from Go 1.14 and earlier:
go tool fix -r eglconf <pkg>
Using cgo directly
Usage:
@ -985,7 +990,7 @@ produces a file named a.out, even if cmd/link does so by invoking the host
linker in external linking mode.
By default, cmd/link will decide the linking mode as follows: if the only
packages using cgo are those on a whitelist of standard library
packages using cgo are those on a list of known standard library
packages (net, os/user, runtime/cgo), cmd/link will use internal linking
mode. Otherwise, there are non-standard cgo packages involved, and cmd/link
will use external linking mode. The first rule means that a build of

View file

@ -200,6 +200,9 @@ func (p *Package) Translate(f *File) {
numTypedefs = len(p.typedefs)
// Also ask about any typedefs we've seen so far.
for _, info := range p.typedefList {
if f.Name[info.typedef] != nil {
continue
}
n := &Name{
Go: info.typedef,
C: info.typedef,
@ -351,7 +354,7 @@ func (p *Package) guessKinds(f *File) []*Name {
// void __cgo_f_xxx_5(void) { static const char __cgo_undefined__5[] = (name); }
//
// If we see an error at not-declared:xxx, the corresponding name is not declared.
// If we see an error at not-type:xxx, the corresponding name is a type.
// If we see an error at not-type:xxx, the corresponding name is not a type.
// If we see an error at not-int-const:xxx, the corresponding name is not an integer constant.
// If we see an error at not-num-const:xxx, the corresponding name is not a number constant.
// If we see an error at not-str-lit:xxx, the corresponding name is not a string literal.
@ -728,6 +731,9 @@ func (p *Package) prepareNames(f *File) {
}
}
p.mangleName(n)
if n.Kind == "type" && typedef[n.Mangle] == nil {
typedef[n.Mangle] = n.Type
}
}
}
@ -1366,6 +1372,9 @@ func (p *Package) rewriteRef(f *File) {
if *godefs {
// Substitute definition for mangled type name.
if r.Name.Type != nil && r.Name.Kind == "type" {
expr = r.Name.Type.Go
}
if id, ok := expr.(*ast.Ident); ok {
if t := typedef[id.Name]; t != nil {
expr = t.Go
@ -1431,9 +1440,7 @@ func (p *Package) rewriteName(f *File, r *Ref) ast.Expr {
r.Context = ctxType
if r.Name.Type == nil {
error_(r.Pos(), "invalid conversion to C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)
break
}
expr = r.Name.Type.Go
break
}
error_(r.Pos(), "call of non-function C.%s", fixGo(r.Name.Go))
@ -1490,9 +1497,7 @@ func (p *Package) rewriteName(f *File, r *Ref) ast.Expr {
// Okay - might be new(T)
if r.Name.Type == nil {
error_(r.Pos(), "expression C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)
break
}
expr = r.Name.Type.Go
case "var":
expr = &ast.StarExpr{Star: (*r.Expr).Pos(), X: expr}
case "macro":
@ -1511,8 +1516,6 @@ func (p *Package) rewriteName(f *File, r *Ref) ast.Expr {
// Use of C.enum_x, C.struct_x or C.union_x without C definition.
// GCC won't raise an error when using pointers to such unknown types.
error_(r.Pos(), "type C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)
} else {
expr = r.Name.Type.Go
}
default:
if r.Name.Kind == "func" {
@ -3036,8 +3039,9 @@ func (c *typeConv) anonymousStructTypedef(dt *dwarf.TypedefType) bool {
return ok && st.StructName == ""
}
// badPointerTypedef reports whether t is a C typedef that should not be considered a pointer in Go.
// A typedef is bad if C code sometimes stores non-pointers in this type.
// badPointerTypedef reports whether dt is a C typedef that should not be
// considered a pointer in Go. A typedef is bad if C code sometimes stores
// non-pointers in this type.
// TODO: Currently our best solution is to find these manually and list them as
// they come up. A better solution is desired.
func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool {
@ -3047,7 +3051,7 @@ func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool {
if c.badJNI(dt) {
return true
}
if c.badEGLDisplay(dt) {
if c.badEGLType(dt) {
return true
}
return false
@ -3186,11 +3190,11 @@ func (c *typeConv) badJNI(dt *dwarf.TypedefType) bool {
return false
}
func (c *typeConv) badEGLDisplay(dt *dwarf.TypedefType) bool {
if dt.Name != "EGLDisplay" {
func (c *typeConv) badEGLType(dt *dwarf.TypedefType) bool {
if dt.Name != "EGLDisplay" && dt.Name != "EGLConfig" {
return false
}
// Check that the typedef is "typedef void *EGLDisplay".
// Check that the typedef is "typedef void *<name>".
if ptr, ok := dt.Type.(*dwarf.PtrType); ok {
if _, ok := ptr.Type.(*dwarf.VoidType); ok {
return true

View file

@ -22,6 +22,7 @@ import (
"regexp"
"sort"
"strings"
"unicode"
)
var (
@ -102,6 +103,11 @@ func (p *Package) writeDefs() {
typedefNames := make([]string, 0, len(typedef))
for name := range typedef {
if name == "_Ctype_void" {
// We provide an appropriate declaration for
// _Ctype_void below (#39877).
continue
}
typedefNames = append(typedefNames, name)
}
sort.Strings(typedefNames)
@ -807,6 +813,28 @@ func (p *Package) packedAttribute() string {
return s + "))"
}
// exportParamName returns the value of param as it should be
// displayed in a c header file. If param contains any non-ASCII
// characters, this function will return the character p followed by
// the value of position; otherwise, this function will return the
// value of param.
func exportParamName(param string, position int) string {
if param == "" {
return fmt.Sprintf("p%d", position)
}
pname := param
for i := 0; i < len(param); i++ {
if param[i] > unicode.MaxASCII {
pname = fmt.Sprintf("p%d", position)
break
}
}
return pname
}
// Write out the various stubs we need to support functions exported
// from Go so that they are callable from C.
func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
@ -920,42 +948,45 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
if i > 0 || fn.Recv != nil {
s += ", "
}
s += fmt.Sprintf("%s p%d", p.cgoType(atype).C, i)
s += fmt.Sprintf("%s %s", p.cgoType(atype).C, exportParamName(aname, i))
})
s += ")"
if len(exp.Doc) > 0 {
fmt.Fprintf(fgcch, "\n%s", exp.Doc)
if !strings.HasSuffix(exp.Doc, "\n") {
fmt.Fprint(fgcch, "\n")
}
}
fmt.Fprintf(fgcch, "\nextern %s;\n", s)
fmt.Fprintf(fgcch, "extern %s;\n", s)
fmt.Fprintf(fgcc, "extern void _cgoexp%s_%s(void *, int, __SIZE_TYPE__);\n", cPrefix, exp.ExpName)
fmt.Fprintf(fgcc, "\nCGO_NO_SANITIZE_THREAD")
fmt.Fprintf(fgcc, "\n%s\n", s)
fmt.Fprintf(fgcc, "{\n")
fmt.Fprintf(fgcc, "\t__SIZE_TYPE__ _cgo_ctxt = _cgo_wait_runtime_init_done();\n")
fmt.Fprintf(fgcc, "\t%s %v a;\n", ctype, p.packedAttribute())
fmt.Fprintf(fgcc, "\t%s %v _cgo_a;\n", ctype, p.packedAttribute())
if gccResult != "void" && (len(fntype.Results.List) > 1 || len(fntype.Results.List[0].Names) > 1) {
fmt.Fprintf(fgcc, "\t%s r;\n", gccResult)
}
if fn.Recv != nil {
fmt.Fprintf(fgcc, "\ta.recv = recv;\n")
fmt.Fprintf(fgcc, "\t_cgo_a.recv = recv;\n")
}
forFieldList(fntype.Params,
func(i int, aname string, atype ast.Expr) {
fmt.Fprintf(fgcc, "\ta.p%d = p%d;\n", i, i)
fmt.Fprintf(fgcc, "\t_cgo_a.p%d = %s;\n", i, exportParamName(aname, i))
})
fmt.Fprintf(fgcc, "\t_cgo_tsan_release();\n")
fmt.Fprintf(fgcc, "\tcrosscall2(_cgoexp%s_%s, &a, %d, _cgo_ctxt);\n", cPrefix, exp.ExpName, off)
fmt.Fprintf(fgcc, "\tcrosscall2(_cgoexp%s_%s, &_cgo_a, %d, _cgo_ctxt);\n", cPrefix, exp.ExpName, off)
fmt.Fprintf(fgcc, "\t_cgo_tsan_acquire();\n")
fmt.Fprintf(fgcc, "\t_cgo_release_context(_cgo_ctxt);\n")
if gccResult != "void" {
if len(fntype.Results.List) == 1 && len(fntype.Results.List[0].Names) <= 1 {
fmt.Fprintf(fgcc, "\treturn a.r0;\n")
fmt.Fprintf(fgcc, "\treturn _cgo_a.r0;\n")
} else {
forFieldList(fntype.Results,
func(i int, aname string, atype ast.Expr) {
fmt.Fprintf(fgcc, "\tr.r%d = a.r%d;\n", i, i)
fmt.Fprintf(fgcc, "\tr.r%d = _cgo_a.r%d;\n", i, i)
})
fmt.Fprintf(fgcc, "\treturn r;\n")
}

View file

@ -35,23 +35,24 @@
//
// Additional help topics:
//
// buildmode build modes
// c calling between Go and C
// cache build and test caching
// environment environment variables
// filetype file types
// go.mod the go.mod file
// gopath GOPATH environment variable
// gopath-get legacy GOPATH go get
// goproxy module proxy protocol
// importpath import path syntax
// modules modules, module versions, and more
// module-get module-aware go get
// module-auth module authentication using go.sum
// module-private module configuration for non-public modules
// packages package lists and patterns
// testflag testing flags
// testfunc testing functions
// buildconstraint build constraints
// buildmode build modes
// c calling between Go and C
// cache build and test caching
// environment environment variables
// filetype file types
// go.mod the go.mod file
// gopath GOPATH environment variable
// gopath-get legacy GOPATH go get
// goproxy module proxy protocol
// importpath import path syntax
// modules modules, module versions, and more
// module-get module-aware go get
// module-auth module authentication using go.sum
// module-private module configuration for non-public modules
// packages package lists and patterns
// testflag testing flags
// testfunc testing functions
//
// Use "go help <topic>" for more information about that topic.
//
@ -547,6 +548,9 @@
// tag "generate" so that files may be examined by go generate but ignored
// during build.
//
// For packages with invalid code, generate processes only source files with a
// valid package clause.
//
// If any generator returns an error exit status, "go generate" skips
// all further processing for that package.
//
@ -657,7 +661,10 @@
// this automatically as well.
//
// The -insecure flag permits fetching from repositories and resolving
// custom domains using insecure schemes such as HTTP. Use with caution.
// custom domains using insecure schemes such as HTTP. Use with caution. The
// GOINSECURE environment variable is usually a better alternative, since it
// provides control over which modules may be retrieved using an insecure scheme.
// See 'go help environment' for details.
//
// The second step is to download (if needed), build, and install
// the named packages.
@ -1017,7 +1024,8 @@
//
// 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.
// With no arguments, download applies to all dependencies of the main module
// (equivalent to 'go mod download all').
//
// The go command will automatically download modules as needed during ordinary
// execution. The "go mod download" command is useful mainly for pre-filling
@ -1305,10 +1313,10 @@
// and its test source files to identify significant problems. If go vet
// finds any problems, go test reports those and does not run the test
// binary. Only a high-confidence subset of the default go vet checks are
// used. That subset is: 'atomic', 'bool', 'buildtags', 'nilfunc', and
// 'printf'. You can see the documentation for these and other vet tests
// via "go doc cmd/vet". To disable the running of go vet, use the
// -vet=off flag.
// used. That subset is: 'atomic', 'bool', 'buildtags', 'errorsas',
// 'ifaceassert', 'nilfunc', 'printf', and 'stringintconv'. You can see
// the documentation for these and other vet tests via "go doc cmd/vet".
// To disable the running of go vet, use the -vet=off flag.
//
// All test output and summary lines are printed to the go command's
// standard output, even if the test printed them to its own standard
@ -1470,6 +1478,95 @@
// See also: go fmt, go fix.
//
//
// Build constraints
//
// A build constraint, also known as a build tag, is a line comment that begins
//
// // +build
//
// that lists the conditions under which a file should be included in the package.
// Constraints may appear in any kind of source file (not just Go), but
// they must appear near the top of the file, preceded
// only by blank lines and other line comments. These rules mean that in Go
// files a build constraint must appear before the package clause.
//
// To distinguish build constraints from package documentation, a series of
// build constraints must be followed by a blank line.
//
// A build constraint is evaluated as the OR of space-separated options.
// Each option evaluates as the AND of its comma-separated terms.
// Each term consists of letters, digits, underscores, and dots.
// A term may be negated with a preceding !.
// For example, the build constraint:
//
// // +build linux,386 darwin,!cgo
//
// corresponds to the boolean formula:
//
// (linux AND 386) OR (darwin AND (NOT cgo))
//
// A file may have multiple build constraints. The overall constraint is the AND
// of the individual constraints. That is, the build constraints:
//
// // +build linux darwin
// // +build amd64
//
// corresponds to the boolean formula:
//
// (linux OR darwin) AND amd64
//
// During a particular build, the following words are satisfied:
//
// - the target operating system, as spelled by runtime.GOOS, set with the
// GOOS environment variable.
// - the target architecture, as spelled by runtime.GOARCH, set with the
// GOARCH environment variable.
// - the compiler being used, either "gc" or "gccgo"
// - "cgo", if the cgo command is supported (see CGO_ENABLED in
// 'go help environment').
// - a term for each Go major release, through the current version:
// "go1.1" from Go version 1.1 onward, "go1.12" from Go 1.12, and so on.
// - any additional tags given by the -tags flag (see 'go help build').
//
// There are no separate build tags for beta or minor releases.
//
// If a file's name, after stripping the extension and a possible _test suffix,
// matches any of the following patterns:
// *_GOOS
// *_GOARCH
// *_GOOS_GOARCH
// (example: source_windows_amd64.go) where GOOS and GOARCH represent
// any known operating system and architecture values respectively, then
// the file is considered to have an implicit build constraint requiring
// those terms (in addition to any explicit constraints in the file).
//
// Using GOOS=android matches build tags and files as for GOOS=linux
// in addition to android tags and files.
//
// Using GOOS=illumos matches build tags and files as for GOOS=solaris
// in addition to illumos tags and files.
//
// To keep a file from being considered for the build:
//
// // +build ignore
//
// (any other unsatisfied word will work as well, but "ignore" is conventional.)
//
// To build a file only when using cgo, and only on Linux and OS X:
//
// // +build linux,cgo darwin,cgo
//
// Such a file is usually paired with another file implementing the
// default functionality for other systems, which in this case would
// carry the constraint:
//
// // +build !linux,!darwin !cgo
//
// Naming a file dns_windows.go will cause it to be included only when
// building the package for Windows; similarly, math_386.s will be included
// only when building the package for 32-bit x86.
//
//
// Build modes
//
// The 'go build' and 'go install' commands take a -buildmode argument which
@ -1603,6 +1700,8 @@
// GOCACHE
// The directory where the go command will store cached
// information for reuse in future builds.
// GOMODCACHE
// The directory where the go command will store downloaded modules.
// GODEBUG
// Enable various debugging facilities. See 'go doc runtime'
// for details.
@ -1620,6 +1719,9 @@
// Comma-separated list of glob patterns (in the syntax of Go's path.Match)
// of module path prefixes that should always be fetched in an insecure
// manner. Only applies to dependencies that are being fetched directly.
// Unlike the -insecure flag on 'go get', GOINSECURE does not disable
// checksum database validation. GOPRIVATE or GONOSUMDB may be used
// to achieve that.
// GOOS
// The operating system for which to compile code.
// Examples are linux, darwin, windows, netbsd.
@ -2693,15 +2795,15 @@
// Go module mirror run by Google and fall back to a direct connection
// if the proxy reports that it does not have the module (HTTP error 404 or 410).
// See https://proxy.golang.org/privacy for the service's privacy policy.
// If GOPROXY is set to the string "direct", downloads use a direct connection
// to source control servers. Setting GOPROXY to "off" disallows downloading
// modules from any source. Otherwise, GOPROXY is expected to be a comma-separated
// list of the URLs of module proxies, in which case the go command will fetch
// modules from those proxies. For each request, the go command tries each proxy
// in sequence, only moving to the next if the current proxy returns a 404 or 410
// HTTP response. The string "direct" may appear in the proxy list,
// to cause a direct connection to be attempted at that point in the search.
// Any proxies listed after "direct" are never consulted.
//
// If GOPROXY is set to the string "direct", downloads use a direct connection to
// source control servers. Setting GOPROXY to "off" disallows downloading modules
// from any source. Otherwise, GOPROXY is expected to be list of module proxy URLs
// separated by either comma (,) or pipe (|) characters, which control error
// fallback behavior. For each request, the go command tries each proxy in
// sequence. If there is an error, the go command will try the next proxy in the
// list if the error is a 404 or 410 HTTP response or if the current proxy is
// followed by a pipe character, indicating it is safe to fall back on any error.
//
// The GOPRIVATE and GONOPROXY environment variables allow bypassing
// the proxy for selected modules. See 'go help module-private' for details.

File diff suppressed because it is too large Load diff

View file

@ -14,6 +14,8 @@ import (
)
func TestDocsUpToDate(t *testing.T) {
t.Parallel()
if !modload.Enabled() {
t.Skipf("help.Help in GOPATH mode is configured by main.main")
}

View file

@ -10,10 +10,12 @@ import "net/http"
// AddCredentials fills in the user's credentials for req, if any.
// The return value reports whether any matching credentials were found.
func AddCredentials(req *http.Request) (added bool) {
host := req.URL.Hostname()
// TODO(golang.org/issue/26232): Support arbitrary user-provided credentials.
netrcOnce.Do(readNetrc)
for _, l := range netrc {
if l.machine == req.URL.Host {
if l.machine == host {
req.SetBasicAuth(l.login, l.password)
return true
}

View file

@ -7,11 +7,8 @@
package base
import (
"bytes"
"errors"
"flag"
"fmt"
"go/scanner"
"log"
"os"
"os/exec"
@ -172,25 +169,3 @@ func RunStdin(cmdline []string) {
// Usage is the usage-reporting function, filled in by package main
// but here for reference by other packages.
var Usage func()
// ExpandScanner expands a scanner.List error into all the errors in the list.
// The default Error method only shows the first error
// and does not shorten paths.
func ExpandScanner(err error) error {
// Look for parser errors.
if err, ok := err.(scanner.ErrorList); ok {
// Prepare error with \n before each message.
// When printed in something like context: %v
// this will put the leading file positions each on
// its own line. It will also show all the errors
// instead of just the first, as err.Error does.
var buf bytes.Buffer
for _, e := range err {
e.Pos.Filename = ShortPath(e.Pos.Filename)
buf.WriteString("\n")
buf.WriteString(e.Error())
}
return errors.New(buf.String())
}
return err
}

View file

@ -4,12 +4,12 @@
package base
// EnvForDir returns a modified environment suitable for running in the given
// directory.
// The environment is the supplied base environment but with an updated $PWD, so
// that an os.Getwd in the child will be faster.
func EnvForDir(dir string, base []string) []string {
// Internally we only use rooted paths, so dir is rooted.
// Even if dir is not rooted, no harm done.
// AppendPWD returns the result of appending PWD=dir to the environment base.
//
// The resulting environment makes os.Getwd more efficient for a subprocess
// running in dir.
func AppendPWD(base []string, dir string) []string {
// Internally we only use absolute paths, so dir is absolute.
// Even if dir is not absolute, no harm done.
return append(base, "PWD="+dir)
}

View file

@ -102,7 +102,7 @@ type boolFlag interface {
}
// SetFromGOFLAGS sets the flags in the given flag set using settings in $GOFLAGS.
func SetFromGOFLAGS(flags flag.FlagSet) {
func SetFromGOFLAGS(flags *flag.FlagSet) {
InitGOFLAGS()
// This loop is similar to flag.Parse except that it ignores
@ -125,14 +125,18 @@ func SetFromGOFLAGS(flags flag.FlagSet) {
if f == nil {
continue
}
// Use flags.Set consistently (instead of f.Value.Set) so that a subsequent
// call to flags.Visit will correctly visit the flags that have been set.
if fb, ok := f.Value.(boolFlag); ok && fb.IsBoolFlag() {
if hasValue {
if err := fb.Set(value); err != nil {
if err := flags.Set(f.Name, 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 {
if err := flags.Set(f.Name, "true"); err != nil {
fmt.Fprintf(flags.Output(), "go: invalid boolean flag %s (from %s): %v\n", name, where, err)
flags.Usage()
}
@ -142,7 +146,7 @@ func SetFromGOFLAGS(flags flag.FlagSet) {
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 {
if err := flags.Set(f.Name, 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

@ -108,7 +108,7 @@ const (
// GODEBUG=gocacheverify=1.
var verify = false
var errVerifyMode = errors.New("gocachverify=1")
var errVerifyMode = errors.New("gocacheverify=1")
// DebugTest is set when GODEBUG=gocachetest=1 is in the environment.
var DebugTest = false

View file

@ -236,6 +236,7 @@ var (
GOROOTpkg = filepath.Join(GOROOT, "pkg")
GOROOTsrc = filepath.Join(GOROOT, "src")
GOROOT_FINAL = findGOROOT_FINAL()
GOMODCACHE = envOr("GOMODCACHE", gopathDir("pkg/mod"))
// Used in envcmd.MkEnv and build ID computations.
GOARM = envOr("GOARM", fmt.Sprint(objabi.GOARM))
@ -253,6 +254,8 @@ var (
GOINSECURE = Getenv("GOINSECURE")
)
var SumdbDir = gopathDir("pkg/sumdb")
// GetArchEnv returns the name and setting of the
// GOARCH-specific architecture environment variable.
// If the current architecture has no GOARCH-specific variable,
@ -364,3 +367,11 @@ func isGOROOT(path string) bool {
}
return stat.IsDir()
}
func gopathDir(rel string) string {
list := filepath.SplitList(BuildContext.GOPATH)
if len(list) == 0 || list[0] == "" {
return ""
}
return filepath.Join(list[0], rel)
}

View file

@ -137,20 +137,27 @@ func runClean(cmd *base.Command, args []string) {
if cfg.BuildN || cfg.BuildX {
b.Showcmd("", "rm -r %s", strings.Join(subdirs, " "))
}
for _, d := range subdirs {
// Only print the first error - there may be many.
// This also mimics what os.RemoveAll(dir) would do.
if err := os.RemoveAll(d); err != nil && !printedErrors {
printedErrors = true
base.Errorf("go clean -cache: %v", err)
if !cfg.BuildN {
for _, d := range subdirs {
// Only print the first error - there may be many.
// This also mimics what os.RemoveAll(dir) would do.
if err := os.RemoveAll(d); err != nil && !printedErrors {
printedErrors = true
base.Errorf("go clean -cache: %v", err)
}
}
}
}
logFile := filepath.Join(dir, "log.txt")
if err := os.RemoveAll(logFile); err != nil && !printedErrors {
printedErrors = true
base.Errorf("go clean -cache: %v", err)
if cfg.BuildN || cfg.BuildX {
b.Showcmd("", "rm -f %s", logFile)
}
if !cfg.BuildN {
if err := os.RemoveAll(logFile); err != nil && !printedErrors {
printedErrors = true
base.Errorf("go clean -cache: %v", err)
}
}
}
}
@ -186,14 +193,14 @@ func runClean(cmd *base.Command, args []string) {
}
if cleanModcache {
if modfetch.PkgMod == "" {
if cfg.GOMODCACHE == "" {
base.Fatalf("go clean -modcache: no module cache")
}
if cfg.BuildN || cfg.BuildX {
b.Showcmd("", "rm -rf %s", modfetch.PkgMod)
b.Showcmd("", "rm -rf %s", cfg.GOMODCACHE)
}
if !cfg.BuildN {
if err := modfetch.RemoveAll(modfetch.PkgMod); err != nil {
if err := modfetch.RemoveAll(cfg.GOMODCACHE); err != nil {
base.Errorf("go clean -modcache: %v", err)
}
}
@ -232,7 +239,7 @@ func clean(p *load.Package) {
cleaned[p] = true
if p.Dir == "" {
base.Errorf("can't load package: %v", p.Error)
base.Errorf("%v", p.Error)
return
}
dirs, err := ioutil.ReadDir(p.Dir)

View file

@ -6,13 +6,10 @@
package cmdflag
import (
"errors"
"flag"
"fmt"
"os"
"strconv"
"strings"
"cmd/go/internal/base"
)
// The flag handling part of go commands such as test is large and distracting.
@ -20,141 +17,113 @@ import (
// our command line are for us, and some are for the binary we're running,
// and some are for both.
// Defn defines a flag we know about.
type Defn struct {
Name string // Name on command line.
BoolVar *bool // If it's a boolean flag, this points to it.
Value flag.Value // The flag.Value represented.
PassToTest bool // Pass to the test binary? Used only by go test.
Present bool // Flag has been seen.
// ErrFlagTerminator indicates the distinguished token "--", which causes the
// flag package to treat all subsequent arguments as non-flags.
var ErrFlagTerminator = errors.New("flag terminator")
// A FlagNotDefinedError indicates a flag-like argument that does not correspond
// to any registered flag in a FlagSet.
type FlagNotDefinedError struct {
RawArg string // the original argument, like --foo or -foo=value
Name string
HasValue bool // is this the -foo=value or --foo=value form?
Value string // only provided if HasValue is true
}
// IsBool reports whether v is a bool flag.
func IsBool(v flag.Value) bool {
vv, ok := v.(interface {
IsBoolFlag() bool
})
if ok {
return vv.IsBoolFlag()
}
return false
func (e FlagNotDefinedError) Error() string {
return fmt.Sprintf("flag provided but not defined: -%s", e.Name)
}
// SetBool sets the addressed boolean to the value.
func SetBool(cmd string, flag *bool, value string) {
x, err := strconv.ParseBool(value)
if err != nil {
SyntaxError(cmd, "illegal bool flag value "+value)
}
*flag = x
// A NonFlagError indicates an argument that is not a syntactically-valid flag.
type NonFlagError struct {
RawArg string
}
// SetInt sets the addressed integer to the value.
func SetInt(cmd string, flag *int, value string) {
x, err := strconv.Atoi(value)
if err != nil {
SyntaxError(cmd, "illegal int flag value "+value)
}
*flag = x
func (e NonFlagError) Error() string {
return fmt.Sprintf("not a flag: %q", e.RawArg)
}
// SyntaxError reports an argument syntax error and exits the program.
func SyntaxError(cmd, msg string) {
fmt.Fprintf(os.Stderr, "go %s: %s\n", cmd, msg)
if cmd == "test" {
fmt.Fprintf(os.Stderr, `run "go help %s" or "go help testflag" for more information`+"\n", cmd)
} else {
fmt.Fprintf(os.Stderr, `run "go help %s" for more information`+"\n", cmd)
}
base.SetExitStatus(2)
base.Exit()
}
// ParseOne sees if args[0] is present in the given flag set and if so,
// sets its value and returns the flag along with the remaining (unused) arguments.
//
// ParseOne always returns either a non-nil Flag or a non-nil error,
// and always consumes at least one argument (even on error).
//
// Unlike (*flag.FlagSet).Parse, ParseOne does not log its own errors.
func ParseOne(fs *flag.FlagSet, args []string) (f *flag.Flag, remainingArgs []string, err error) {
// This function is loosely derived from (*flag.FlagSet).parseOne.
// 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)
raw, args := args[0], args[1:]
arg := raw
if strings.HasPrefix(arg, "--") {
if arg == "--" {
return nil, args, ErrFlagTerminator
}
arg = arg[1:] // reduce two minuses to one
}
}
// 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.Name()+".") it is ignored for the purpose of this function.
func Parse(cmd string, usage func(), defns []*Defn, args []string, i int) (f *Defn, value string, extra bool) {
arg := args[i]
if strings.HasPrefix(arg, "--") { // reduce two minuses to one
arg = arg[1:]
}
switch arg {
case "-?", "-h", "-help":
usage()
return nil, args, flag.ErrHelp
}
if arg == "" || arg[0] != '-' {
return
if len(arg) < 2 || arg[0] != '-' || arg[1] == '-' || arg[1] == '=' {
return nil, args, NonFlagError{RawArg: raw}
}
name := arg[1:]
// If there's already a prefix such as "test.", drop it for now.
name = strings.TrimPrefix(name, cmd+".")
equals := strings.Index(name, "=")
if equals >= 0 {
value = name[equals+1:]
name = name[:equals]
hasValue := false
value := ""
if i := strings.Index(name, "="); i >= 0 {
value = name[i+1:]
hasValue = true
name = name[0:i]
}
for _, f = range defns {
if name == f.Name {
// Booleans are special because they have modes -x, -x=true, -x=false.
if f.BoolVar != nil || IsBool(f.Value) {
if equals < 0 { // Otherwise, it's been set and will be verified in SetBool.
value = "true"
} else {
// verify it parses
SetBool(cmd, new(bool), value)
}
} else { // Non-booleans must have a value.
extra = equals < 0
if extra {
if i+1 >= len(args) {
SyntaxError(cmd, "missing argument for flag "+f.Name)
}
value = args[i+1]
}
}
if f.Present {
SyntaxError(cmd, f.Name+" flag may be set only once")
}
f.Present = true
return
f = fs.Lookup(name)
if f == nil {
return nil, args, FlagNotDefinedError{
RawArg: raw,
Name: name,
HasValue: hasValue,
Value: value,
}
}
f = nil
return
// Use fs.Set instead of f.Value.Set below so that any subsequent call to
// fs.Visit will correctly visit the flags that have been set.
failf := func(format string, a ...interface{}) (*flag.Flag, []string, error) {
return f, args, fmt.Errorf(format, a...)
}
if fv, ok := f.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn't need an arg
if hasValue {
if err := fs.Set(name, value); err != nil {
return failf("invalid boolean value %q for -%s: %v", value, name, err)
}
} else {
if err := fs.Set(name, "true"); err != nil {
return failf("invalid boolean flag %s: %v", name, err)
}
}
} else {
// It must have a value, which might be the next argument.
if !hasValue && len(args) > 0 {
// value is the next arg
hasValue = true
value, args = args[0], args[1:]
}
if !hasValue {
return failf("flag needs an argument: -%s", name)
}
if err := fs.Set(name, value); err != nil {
return failf("invalid value %q for flag -%s: %v", value, name, err)
}
}
return f, args, nil
}
// 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
type boolFlag interface {
IsBoolFlag() bool
}

View file

@ -77,6 +77,7 @@ func MkEnv() []cfg.EnvVar {
{Name: "GOHOSTARCH", Value: runtime.GOARCH},
{Name: "GOHOSTOS", Value: runtime.GOOS},
{Name: "GOINSECURE", Value: cfg.GOINSECURE},
{Name: "GOMODCACHE", Value: cfg.GOMODCACHE},
{Name: "GONOPROXY", Value: cfg.GONOPROXY},
{Name: "GONOSUMDB", Value: cfg.GONOSUMDB},
{Name: "GOOS", Value: cfg.Goos},

View file

@ -6,11 +6,11 @@
package fmtcmd
import (
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"cmd/go/internal/base"
@ -72,11 +72,12 @@ func runFmt(cmd *base.Command, args []string) {
continue
}
if pkg.Error != nil {
if strings.HasPrefix(pkg.Error.Err.Error(), "build constraints exclude all Go files") {
var nogo *load.NoGoError
if errors.As(pkg.Error, &nogo) && len(pkg.InternalAllGoFiles()) > 0 {
// Skip this error, as we will format
// all files regardless.
} else {
base.Errorf("can't load package: %s", pkg.Error)
base.Errorf("%v", pkg.Error)
continue
}
}

View file

@ -9,7 +9,10 @@ import (
"bufio"
"bytes"
"fmt"
"go/parser"
"go/token"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
@ -119,6 +122,9 @@ in the file, one at a time. The go generate tool also sets the build
tag "generate" so that files may be examined by go generate but ignored
during build.
For packages with invalid code, generate processes only source files with a
valid package clause.
If any generator returns an error exit status, "go generate" skips
all further processing for that package.
@ -169,7 +175,7 @@ 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) {
for _, pkg := range load.PackagesAndErrors(args) {
if modload.Enabled() && pkg.Module != nil && !pkg.Module.Main {
if !printed {
fmt.Fprintf(os.Stderr, "go: not generating in packages in dependency modules\n")
@ -178,18 +184,14 @@ func runGenerate(cmd *base.Command, args []string) {
continue
}
pkgName := pkg.Name
for _, file := range pkg.InternalGoFiles() {
if !generate(pkgName, file) {
if !generate(file) {
break
}
}
pkgName += "_test"
for _, file := range pkg.InternalXGoFiles() {
if !generate(pkgName, file) {
if !generate(file) {
break
}
}
@ -197,16 +199,23 @@ func runGenerate(cmd *base.Command, args []string) {
}
// generate runs the generation directives for a single file.
func generate(pkg, absFile string) bool {
fd, err := os.Open(absFile)
func generate(absFile string) bool {
src, err := ioutil.ReadFile(absFile)
if err != nil {
log.Fatalf("generate: %s", err)
}
defer fd.Close()
// Parse package clause
filePkg, err := parser.ParseFile(token.NewFileSet(), "", src, parser.PackageClauseOnly)
if err != nil {
// Invalid package clause - ignore file.
return true
}
g := &Generator{
r: fd,
r: bytes.NewReader(src),
path: absFile,
pkg: pkg,
pkg: filePkg.Name.String(),
commands: make(map[string][]string),
}
return g.run()

View file

@ -7,7 +7,6 @@ package get
import (
"fmt"
"go/build"
"os"
"path/filepath"
"runtime"
@ -194,12 +193,28 @@ func downloadPaths(patterns []string) []string {
for _, arg := range patterns {
if strings.Contains(arg, "@") {
base.Fatalf("go: cannot use path@version syntax in GOPATH mode")
continue
}
// Guard against 'go get x.go', a common mistake.
// Note that package and module paths may end with '.go', so only print an error
// if the argument has no slash or refers to an existing file.
if strings.HasSuffix(arg, ".go") {
if !strings.Contains(arg, "/") {
base.Errorf("go get %s: arguments must be package or module paths", arg)
continue
}
if fi, err := os.Stat(arg); err == nil && !fi.IsDir() {
base.Errorf("go get: %s exists as a file, but 'go get' requires package arguments", arg)
}
}
}
base.ExitIfErrors()
var pkgs []string
for _, m := range search.ImportPathsQuiet(patterns) {
if len(m.Pkgs) == 0 && strings.Contains(m.Pattern, "...") {
pkgs = append(pkgs, m.Pattern)
if len(m.Pkgs) == 0 && strings.Contains(m.Pattern(), "...") {
pkgs = append(pkgs, m.Pattern())
} else {
pkgs = append(pkgs, m.Pkgs...)
}
@ -285,10 +300,16 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int)
// We delay this until after reloadPackage so that the old entry
// for p has been replaced in the package cache.
if wildcardOkay && strings.Contains(arg, "...") {
if build.IsLocalImport(arg) {
args = search.MatchPackagesInFS(arg).Pkgs
match := search.NewMatch(arg)
if match.IsLocal() {
match.MatchDirs()
args = match.Dirs
} else {
args = search.MatchPackages(arg).Pkgs
match.MatchPackages()
args = match.Pkgs
}
for _, err := range match.Errs {
base.Errorf("%s", err)
}
isWildcard = true
}

View file

@ -430,7 +430,7 @@ func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool)
cmd := exec.Command(v.cmd, args...)
cmd.Dir = dir
cmd.Env = base.EnvForDir(cmd.Dir, os.Environ())
cmd.Env = base.AppendPWD(os.Environ(), cmd.Dir)
if cfg.BuildX {
fmt.Fprintf(os.Stderr, "cd %s\n", dir)
fmt.Fprintf(os.Stderr, "%s %s\n", v.cmd, strings.Join(args, " "))

View file

@ -93,7 +93,7 @@ Use "go help{{with .LongName}} {{.}}{{end}} <command>" for more information abou
{{if eq (.UsageLine) "go"}}
Additional help topics:
{{range .Commands}}{{if and (not .Runnable) (not .Commands)}}
{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
{{.Name | printf "%-15s"}} {{.Short}}{{end}}{{end}}
Use "go help{{with .LongName}} {{.}}{{end}} <topic>" for more information about that topic.
{{end}}

View file

@ -493,6 +493,8 @@ General-purpose environment variables:
GOCACHE
The directory where the go command will store cached
information for reuse in future builds.
GOMODCACHE
The directory where the go command will store downloaded modules.
GODEBUG
Enable various debugging facilities. See 'go doc runtime'
for details.
@ -510,6 +512,9 @@ General-purpose environment variables:
Comma-separated list of glob patterns (in the syntax of Go's path.Match)
of module path prefixes that should always be fetched in an insecure
manner. Only applies to dependencies that are being fetched directly.
Unlike the -insecure flag on 'go get', GOINSECURE does not disable
checksum database validation. GOPRIVATE or GONOSUMDB may be used
to achieve that.
GOOS
The operating system for which to compile code.
Examples are linux, darwin, windows, netbsd.
@ -762,3 +767,95 @@ GODEBUG=gocachetest=1 causes the go command to print details of its
decisions about whether to reuse a cached test result.
`,
}
var HelpBuildConstraint = &base.Command{
UsageLine: "buildconstraint",
Short: "build constraints",
Long: `
A build constraint, also known as a build tag, is a line comment that begins
// +build
that lists the conditions under which a file should be included in the package.
Constraints may appear in any kind of source file (not just Go), but
they must appear near the top of the file, preceded
only by blank lines and other line comments. These rules mean that in Go
files a build constraint must appear before the package clause.
To distinguish build constraints from package documentation, a series of
build constraints must be followed by a blank line.
A build constraint is evaluated as the OR of space-separated options.
Each option evaluates as the AND of its comma-separated terms.
Each term consists of letters, digits, underscores, and dots.
A term may be negated with a preceding !.
For example, the build constraint:
// +build linux,386 darwin,!cgo
corresponds to the boolean formula:
(linux AND 386) OR (darwin AND (NOT cgo))
A file may have multiple build constraints. The overall constraint is the AND
of the individual constraints. That is, the build constraints:
// +build linux darwin
// +build amd64
corresponds to the boolean formula:
(linux OR darwin) AND amd64
During a particular build, the following words are satisfied:
- the target operating system, as spelled by runtime.GOOS, set with the
GOOS environment variable.
- the target architecture, as spelled by runtime.GOARCH, set with the
GOARCH environment variable.
- the compiler being used, either "gc" or "gccgo"
- "cgo", if the cgo command is supported (see CGO_ENABLED in
'go help environment').
- a term for each Go major release, through the current version:
"go1.1" from Go version 1.1 onward, "go1.12" from Go 1.12, and so on.
- any additional tags given by the -tags flag (see 'go help build').
There are no separate build tags for beta or minor releases.
If a file's name, after stripping the extension and a possible _test suffix,
matches any of the following patterns:
*_GOOS
*_GOARCH
*_GOOS_GOARCH
(example: source_windows_amd64.go) where GOOS and GOARCH represent
any known operating system and architecture values respectively, then
the file is considered to have an implicit build constraint requiring
those terms (in addition to any explicit constraints in the file).
Using GOOS=android matches build tags and files as for GOOS=linux
in addition to android tags and files.
Using GOOS=illumos matches build tags and files as for GOOS=solaris
in addition to illumos tags and files.
To keep a file from being considered for the build:
// +build ignore
(any other unsatisfied word will work as well, but "ignore" is conventional.)
To build a file only when using cgo, and only on Linux and OS X:
// +build linux,cgo darwin,cgo
Such a file is usually paired with another file implementing the
default functionality for other systems, which in this case would
carry the constraint:
// +build !linux,!darwin !cgo
Naming a file dns_windows.go will cause it to be included only when
building the package for Windows; similarly, math_386.s will be included
only when building the package for 32-bit x86.
`,
}

View file

@ -451,6 +451,7 @@ func runList(cmd *base.Command, args []string) {
pkgs = load.PackagesAndErrors(args)
} else {
pkgs = load.Packages(args)
base.ExitIfErrors()
}
if cache.Default() == nil {
@ -471,9 +472,6 @@ func runList(cmd *base.Command, args []string) {
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 {
var pmain, ptest, pxtest *load.Package
var err error

View file

@ -187,20 +187,17 @@ type PackageInternal struct {
Gccgoflags []string // -gccgoflags for this package
}
// A NoGoError indicates that no Go files for the package were applicable to the
// build for that package.
//
// That may be because there were no files whatsoever, or because all files were
// excluded, or because all non-excluded files were test sources.
type NoGoError struct {
Package *Package
}
func (e *NoGoError) Error() string {
// Count files beginning with _ and ., which we will pretend don't exist at all.
dummy := 0
for _, name := range e.Package.IgnoredGoFiles {
if strings.HasPrefix(name, "_") || strings.HasPrefix(name, ".") {
dummy++
}
}
if len(e.Package.IgnoredGoFiles) > dummy {
if len(e.Package.constraintIgnoredGoFiles()) > 0 {
// Go files exist, but they were ignored due to build constraints.
return "build constraints exclude all Go files in " + e.Package.Dir
}
@ -213,6 +210,77 @@ func (e *NoGoError) Error() string {
return "no Go files in " + e.Package.Dir
}
// setLoadPackageDataError presents an error found when loading package data
// as a *PackageError. It has special cases for some common errors to improve
// messages shown to users and reduce redundancy.
//
// setLoadPackageDataError returns true if it's safe to load information about
// imported packages, for example, if there was a parse error loading imports
// in one file, but other files are okay.
func (p *Package) setLoadPackageDataError(err error, path string, stk *ImportStack, importPos []token.Position) {
matchErr, isMatchErr := err.(*search.MatchError)
if isMatchErr && matchErr.Match.Pattern() == path {
if matchErr.Match.IsLiteral() {
// The error has a pattern has a pattern similar to the import path.
// It may be slightly different (./foo matching example.com/foo),
// but close enough to seem redundant.
// Unwrap the error so we don't show the pattern.
err = matchErr.Err
}
}
// Replace (possibly wrapped) *build.NoGoError with *load.NoGoError.
// The latter is more specific about the cause.
var nogoErr *build.NoGoError
if errors.As(err, &nogoErr) {
if p.Dir == "" && nogoErr.Dir != "" {
p.Dir = nogoErr.Dir
}
err = &NoGoError{Package: p}
}
// Report the error on the importing package if the problem is with the import declaration
// for example, if the package doesn't exist or if the import path is malformed.
// On the other hand, don't include a position if the problem is with the imported package,
// for example there are no Go files (NoGoError), or there's a problem in the imported
// package's source files themselves.
//
// TODO(matloob): Perhaps make each of those the errors in the first group
// (including modload.ImportMissingError, and the corresponding
// "cannot find package %q in any of" GOPATH-mode error
// produced in build.(*Context).Import; modload.AmbiguousImportError,
// and modload.PackageNotInModuleError; and the malformed module path errors
// produced in golang.org/x/mod/module.CheckMod) implement an interface
// to make it easier to check for them? That would save us from having to
// move the modload errors into this package to avoid a package import cycle,
// and from having to export an error type for the errors produced in build.
if !isMatchErr && nogoErr != nil {
stk.Push(path)
defer stk.Pop()
}
// Take only the first error from a scanner.ErrorList. PackageError only
// has room for one position, so we report the first error with a position
// instead of all of the errors without a position.
var pos string
if scanErr, ok := err.(scanner.ErrorList); ok && len(scanErr) > 0 {
scanPos := scanErr[0].Pos
scanPos.Filename = base.ShortPath(scanPos.Filename)
pos = scanPos.String()
err = errors.New(scanErr[0].Msg)
}
p.Error = &PackageError{
ImportStack: stk.Copy(),
Pos: pos,
Err: err,
}
if path != stk.Top() {
p = setErrorPos(p, importPos)
}
}
// Resolve returns the resolved version of imports,
// which should be p.TestImports or p.XTestImports, NOT p.Imports.
// The imports in p.TestImports and p.XTestImports are not recursively
@ -304,19 +372,16 @@ func (p *Package) copyBuild(pp *build.Package) {
// A PackageError describes an error loading information about a package.
type PackageError struct {
ImportStack []string // shortest path from package named on command line to this one
Pos string // position of error
Err error // the error itself
IsImportCycle bool // the error is an import cycle
Hard bool // whether the error is soft or hard; soft errors are ignored in some places
ImportStack []string // shortest path from package named on command line to this one
Pos string // position of error
Err error // the error itself
IsImportCycle bool // the error is an import cycle
Hard bool // whether the error is soft or hard; soft errors are ignored in some places
alwaysPrintStack bool // whether to always print the ImportStack
}
func (p *PackageError) Error() string {
// Import cycles deserve special treatment.
if p.IsImportCycle {
return fmt.Sprintf("%s\npackage %s\n", p.Err, strings.Join(p.ImportStack, "\n\timports "))
}
if p.Pos != "" {
if p.Pos != "" && (len(p.ImportStack) == 0 || !p.alwaysPrintStack) {
// Omit import stack. The full path to the file where the error
// is the most important thing.
return p.Pos + ": " + p.Err.Error()
@ -328,17 +393,18 @@ func (p *PackageError) Error() string {
// last path on the stack, we don't omit the path. An error like
// "package A imports B: error loading C caused by B" would not be clearer
// if "imports B" were omitted.
stack := p.ImportStack
var ierr ImportPathError
if len(stack) > 0 && errors.As(p.Err, &ierr) && ierr.ImportPath() == stack[len(stack)-1] {
stack = stack[:len(stack)-1]
}
if len(stack) == 0 {
if len(p.ImportStack) == 0 {
return p.Err.Error()
}
return "package " + strings.Join(stack, "\n\timports ") + ": " + p.Err.Error()
var optpos string
if p.Pos != "" {
optpos = "\n\t" + p.Pos
}
return "package " + strings.Join(p.ImportStack, "\n\timports ") + optpos + ": " + p.Err.Error()
}
func (p *PackageError) Unwrap() error { return p.Err }
// PackageError implements MarshalJSON so that Err is marshaled as a string
// and non-essential fields are omitted.
func (p *PackageError) MarshalJSON() ([]byte, error) {
@ -409,6 +475,13 @@ func (s *ImportStack) Copy() []string {
return append([]string{}, *s...)
}
func (s *ImportStack) Top() string {
if len(*s) == 0 {
return ""
}
return (*s)[len(*s)-1]
}
// shorterThan reports whether sp is shorter than t.
// We use this to record the shortest import sequence
// that leads to a particular package.
@ -536,9 +609,6 @@ func loadImport(pre *preload, path, srcDir string, parent *Package, stk *ImportS
panic("LoadImport called with empty package path")
}
stk.Push(path)
defer stk.Pop()
var parentPath, parentRoot string
parentIsStd := false
if parent != nil {
@ -551,6 +621,11 @@ func loadImport(pre *preload, path, srcDir string, parent *Package, stk *ImportS
pre.preloadImports(bp.Imports, bp)
}
if bp == nil {
if importErr, ok := err.(ImportPathError); !ok || importErr.ImportPath() != path {
// Only add path to the error's import stack if it's not already present on the error.
stk.Push(path)
defer stk.Pop()
}
return &Package{
PackagePublic: PackagePublic{
ImportPath: path,
@ -565,7 +640,9 @@ func loadImport(pre *preload, path, srcDir string, parent *Package, stk *ImportS
importPath := bp.ImportPath
p := packageCache[importPath]
if p != nil {
stk.Push(path)
p = reusePackage(p, stk)
stk.Pop()
} else {
p = new(Package)
p.Internal.Local = build.IsLocalImport(path)
@ -575,17 +652,15 @@ func loadImport(pre *preload, path, srcDir string, parent *Package, stk *ImportS
// Load package.
// loadPackageData may return bp != nil even if an error occurs,
// in order to return partial information.
p.load(stk, bp, err)
if p.Error != nil && p.Error.Pos == "" {
p = setErrorPos(p, importPos)
}
p.load(path, stk, importPos, bp, err)
if !cfg.ModulesEnabled && path != cleanImport(path) {
p.Error = &PackageError{
ImportStack: stk.Copy(),
Err: fmt.Errorf("non-canonical import path: %q should be %q", path, pathpkg.Clean(path)),
Err: ImportErrorf(path, "non-canonical import path %q: should be %q", path, pathpkg.Clean(path)),
}
p.Incomplete = true
setErrorPos(p, importPos)
}
}
@ -594,7 +669,7 @@ func loadImport(pre *preload, path, srcDir string, parent *Package, stk *ImportS
return setErrorPos(perr, importPos)
}
if mode&ResolveImport != 0 {
if perr := disallowVendor(srcDir, path, p, stk); perr != p {
if perr := disallowVendor(srcDir, path, parentPath, p, stk); perr != p {
return setErrorPos(perr, importPos)
}
}
@ -1232,7 +1307,7 @@ func disallowInternal(srcDir string, importer *Package, importerPath string, p *
// as if it were generated into the testing directory tree
// (it's actually in a temporary directory outside any Go tree).
// This cleans up a former kludge in passing functionality to the testing package.
if strings.HasPrefix(p.ImportPath, "testing/internal") && len(*stk) >= 2 && (*stk)[len(*stk)-2] == "testmain" {
if str.HasPathPrefix(p.ImportPath, "testing/internal") && importerPath == "testmain" {
return p
}
@ -1254,11 +1329,10 @@ func disallowInternal(srcDir string, importer *Package, importerPath string, p *
return p
}
// The stack includes p.ImportPath.
// If that's the only thing on the stack, we started
// importerPath is empty: we started
// with a name given on the command line, not an
// import. Anything listed on the command line is fine.
if len(*stk) == 1 {
if importerPath == "" {
return p
}
@ -1307,8 +1381,9 @@ func disallowInternal(srcDir string, importer *Package, importerPath string, p *
// Internal is present, and srcDir is outside parent's tree. Not allowed.
perr := *p
perr.Error = &PackageError{
ImportStack: stk.Copy(),
Err: ImportErrorf(p.ImportPath, "use of internal package "+p.ImportPath+" not allowed"),
alwaysPrintStack: true,
ImportStack: stk.Copy(),
Err: ImportErrorf(p.ImportPath, "use of internal package "+p.ImportPath+" not allowed"),
}
perr.Incomplete = true
return &perr
@ -1336,16 +1411,15 @@ func findInternal(path string) (index int, ok bool) {
// disallowVendor checks that srcDir is allowed to import p as path.
// If the import is allowed, disallowVendor returns the original package p.
// If not, it returns a new package containing just an appropriate error.
func disallowVendor(srcDir string, path string, p *Package, stk *ImportStack) *Package {
// The stack includes p.ImportPath.
// If that's the only thing on the stack, we started
func disallowVendor(srcDir string, path string, importerPath string, p *Package, stk *ImportStack) *Package {
// If the importerPath is empty, we started
// with a name given on the command line, not an
// import. Anything listed on the command line is fine.
if len(*stk) == 1 {
if importerPath == "" {
return p
}
if perr := disallowVendorVisibility(srcDir, p, stk); perr != p {
if perr := disallowVendorVisibility(srcDir, p, importerPath, stk); perr != p {
return perr
}
@ -1368,12 +1442,12 @@ func disallowVendor(srcDir string, path string, p *Package, stk *ImportStack) *P
// is not subject to the rules, only subdirectories of vendor.
// This allows people to have packages and commands named vendor,
// for maximal compatibility with existing source trees.
func disallowVendorVisibility(srcDir string, p *Package, stk *ImportStack) *Package {
// The stack includes p.ImportPath.
// If that's the only thing on the stack, we started
func disallowVendorVisibility(srcDir string, p *Package, importerPath string, stk *ImportStack) *Package {
// The stack does not include p.ImportPath.
// If there's nothing on the stack, we started
// with a name given on the command line, not an
// import. Anything listed on the command line is fine.
if len(*stk) == 1 {
if importerPath == "" {
return p
}
@ -1517,7 +1591,8 @@ func (p *Package) DefaultExecName() string {
// load populates p using information from bp, err, which should
// be the result of calling build.Context.Import.
func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
// stk contains the import stack, not including path itself.
func (p *Package) load(path string, stk *ImportStack, importPos []token.Position, bp *build.Package, err error) {
p.copyBuild(bp)
// The localPrefix is the path we interpret ./ imports relative to.
@ -1535,21 +1610,22 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
ImportStack: stk.Copy(),
Err: err,
}
// Add the importer's position information if the import position exists, and
// the current package being examined is the importer.
// If we have not yet accepted package p onto the import stack,
// then the cause of the error is not within p itself: the error
// must be either in an explicit command-line argument,
// or on the importer side (indicated by a non-empty importPos).
if path != stk.Top() && len(importPos) > 0 {
p = setErrorPos(p, importPos)
}
}
}
if err != nil {
if _, ok := err.(*build.NoGoError); ok {
err = &NoGoError{Package: p}
}
p.Incomplete = true
setError(base.ExpandScanner(err))
if _, isScanErr := err.(scanner.ErrorList); !isScanErr {
return
}
// Fall through if there was an error parsing a file. 'go list -e' should
// still report imports and other metadata.
p.setLoadPackageDataError(err, path, stk, importPos)
}
useBindir := p.Name == "main"
@ -1563,6 +1639,8 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
if useBindir {
// Report an error when the old code.google.com/p/go.tools paths are used.
if InstallTargetDir(p) == StalePath {
// TODO(matloob): remove this branch, and StalePath itself. code.google.com/p/go is so
// old, even this code checking for it is stale now!
newPath := strings.Replace(p.ImportPath, "code.google.com/p/go.", "golang.org/x/", 1)
e := ImportErrorf(p.ImportPath, "the %v command has moved; use %v instead.", p.ImportPath, newPath)
setError(e)
@ -1671,6 +1749,23 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
}
}
// Check for case-insensitive collisions of import paths.
fold := str.ToFold(p.ImportPath)
if other := foldPath[fold]; other == "" {
foldPath[fold] = p.ImportPath
} else if other != p.ImportPath {
setError(ImportErrorf(p.ImportPath, "case-insensitive import collision: %q and %q", p.ImportPath, other))
return
}
if !SafeArg(p.ImportPath) {
setError(ImportErrorf(p.ImportPath, "invalid import path %q", p.ImportPath))
return
}
stk.Push(path)
defer stk.Pop()
// Check for case-insensitive collision of input files.
// To avoid problems on case-insensitive files, we reject any package
// where two different input files have equal names under a case-insensitive
@ -1699,10 +1794,6 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
setError(fmt.Errorf("invalid input directory name %q", name))
return
}
if !SafeArg(p.ImportPath) {
setError(ImportErrorf(p.ImportPath, "invalid import path %q", p.ImportPath))
return
}
// Build list of imported packages and full dependency list.
imports := make([]*Package, 0, len(p.Imports))
@ -1766,15 +1857,6 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
return
}
// Check for case-insensitive collisions of import paths.
fold := str.ToFold(p.ImportPath)
if other := foldPath[fold]; other == "" {
foldPath[fold] = p.ImportPath
} else if other != p.ImportPath {
setError(ImportErrorf(p.ImportPath, "case-insensitive import collision: %q and %q", p.ImportPath, other))
return
}
if cfg.ModulesEnabled && p.Error == nil {
mainPath := p.ImportPath
if p.Internal.CmdlineFiles {
@ -1877,10 +1959,11 @@ func externalLinkingForced(p *Package) bool {
// Some targets must use external linking even inside GOROOT.
switch cfg.BuildContext.GOOS {
case "android":
return true
if cfg.BuildContext.GOARCH != "arm64" {
return true
}
case "darwin":
switch cfg.BuildContext.GOARCH {
case "arm", "arm64":
if cfg.BuildContext.GOARCH == "arm64" {
return true
}
}
@ -1938,13 +2021,22 @@ func (p *Package) InternalXGoFiles() []string {
// using absolute paths. "Possibly relevant" means that files are not excluded
// due to build tags, but files with names beginning with . or _ are still excluded.
func (p *Package) InternalAllGoFiles() []string {
var extra []string
return p.mkAbs(str.StringList(p.constraintIgnoredGoFiles(), p.GoFiles, p.CgoFiles, p.TestGoFiles, p.XTestGoFiles))
}
// constraintIgnoredGoFiles returns the list of Go files ignored for reasons
// other than having a name beginning with '.' or '_'.
func (p *Package) constraintIgnoredGoFiles() []string {
if len(p.IgnoredGoFiles) == 0 {
return nil
}
files := make([]string, 0, len(p.IgnoredGoFiles))
for _, f := range p.IgnoredGoFiles {
if f != "" && f[0] != '.' || f[0] != '_' {
extra = append(extra, f)
if f != "" && f[0] != '.' && f[0] != '_' {
files = append(files, f)
}
}
return p.mkAbs(str.StringList(extra, p.GoFiles, p.CgoFiles, p.TestGoFiles, p.XTestGoFiles))
return files
}
// usesSwig reports whether the package needs to run SWIG.
@ -2038,7 +2130,7 @@ func Packages(args []string) []*Package {
var pkgs []*Package
for _, pkg := range PackagesAndErrors(args) {
if pkg.Error != nil {
base.Errorf("can't load package: %s", pkg.Error)
base.Errorf("%v", pkg.Error)
continue
}
pkgs = append(pkgs, pkg)
@ -2078,13 +2170,13 @@ func PackagesAndErrors(patterns []string) []*Package {
for _, m := range matches {
for _, pkg := range m.Pkgs {
if pkg == "" {
panic(fmt.Sprintf("ImportPaths returned empty package for pattern %s", m.Pattern))
panic(fmt.Sprintf("ImportPaths returned empty package for pattern %s", m.Pattern()))
}
p := loadImport(pre, pkg, base.Cwd, nil, &stk, nil, 0)
p.Match = append(p.Match, m.Pattern)
p.Match = append(p.Match, m.Pattern())
p.Internal.CmdlinePkg = true
if m.Literal {
// Note: do not set = m.Literal unconditionally
if m.IsLiteral() {
// Note: do not set = m.IsLiteral unconditionally
// because maybe we'll see p matching both
// a literal and also a non-literal pattern.
p.Internal.CmdlinePkgLiteral = true
@ -2095,6 +2187,25 @@ func PackagesAndErrors(patterns []string) []*Package {
seenPkg[p] = true
pkgs = append(pkgs, p)
}
if len(m.Errs) > 0 {
// In addition to any packages that were actually resolved from the
// pattern, there was some error in resolving the pattern itself.
// Report it as a synthetic package.
p := new(Package)
p.ImportPath = m.Pattern()
// Pass an empty ImportStack and nil importPos: the error arose from a pattern, not an import.
var stk ImportStack
var importPos []token.Position
p.setLoadPackageDataError(m.Errs[0], m.Pattern(), &stk, importPos)
p.Incomplete = true
p.Match = append(p.Match, m.Pattern())
p.Internal.CmdlinePkg = true
if m.IsLiteral() {
p.Internal.CmdlinePkgLiteral = true
}
pkgs = append(pkgs, p)
}
}
// Now that CmdlinePkg is set correctly,
@ -2130,7 +2241,7 @@ func PackagesForBuild(args []string) []*Package {
printed := map[*PackageError]bool{}
for _, pkg := range pkgs {
if pkg.Error != nil {
base.Errorf("can't load package: %s", pkg.Error)
base.Errorf("%v", pkg.Error)
printed[pkg.Error] = true
}
for _, err := range pkg.DepsErrors {
@ -2140,7 +2251,7 @@ func PackagesForBuild(args []string) []*Package {
// Only print each once.
if !printed[err] {
printed[err] = true
base.Errorf("%s", err)
base.Errorf("%v", err)
}
}
}
@ -2232,9 +2343,7 @@ func GoFilesPackage(gofiles []string) *Package {
pkg := new(Package)
pkg.Internal.Local = true
pkg.Internal.CmdlineFiles = true
stk.Push("main")
pkg.load(&stk, bp, err)
stk.Pop()
pkg.load("command-line-arguments", &stk, nil, bp, err)
pkg.Internal.LocalPrefix = dirToImportPath(dir)
pkg.ImportPath = "command-line-arguments"
pkg.Target = ""

View file

@ -6,7 +6,6 @@ package load
import (
"bytes"
"cmd/go/internal/base"
"cmd/go/internal/str"
"errors"
"fmt"
@ -26,6 +25,7 @@ import (
var TestMainDeps = []string{
// Dependencies for testmain.
"os",
"reflect",
"testing",
"testing/internal/testdeps",
}
@ -55,7 +55,6 @@ func TestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Packag
}
if len(p1.DepsErrors) > 0 {
perr := p1.DepsErrors[0]
perr.Pos = "" // show full import stack
err = perr
break
}
@ -271,7 +270,7 @@ func TestPackagesAndErrors(p *Package, cover *TestCover) (pmain, ptest, pxtest *
// afterward that gathers t.Cover information.
t, err := loadTestFuncs(ptest)
if err != nil && pmain.Error == nil {
pmain.Error = &PackageError{Err: err}
pmain.setLoadPackageDataError(err, p.ImportPath, &stk, nil)
}
t.Cover = cover
if len(ptest.GoFiles)+len(ptest.CgoFiles) > 0 {
@ -540,7 +539,7 @@ 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)
return err
}
for _, d := range f.Decls {
n, ok := d.(*ast.FuncDecl)
@ -612,8 +611,9 @@ var testmainTmpl = lazytemplate.New("main", `
package main
import (
{{if not .TestMain}}
"os"
{{if .TestMain}}
"reflect"
{{end}}
"testing"
"testing/internal/testdeps"
@ -704,6 +704,7 @@ func main() {
m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, examples)
{{with .TestMain}}
{{.Package}}.{{.Name}}(m)
os.Exit(int(reflect.ValueOf(m).Elem().FieldByName("exitCode").Int()))
{{else}}
os.Exit(m.Run())
{{end}}

View file

@ -13,19 +13,19 @@
// or an F_OFD_SETLK command for 'fcntl', that allows for better concurrency and
// does not require per-inode bookkeeping in the application.
//
// TODO(bcmills): If we add a build tag for Illumos (see golang.org/issue/20603)
// then Illumos should use F_OFD_SETLK, and the resulting code would be as
// simple as filelock_unix.go. We will still need the code in this file for AIX
// or as long as Oracle Solaris provides only F_SETLK.
// TODO(golang.org/issue/35618): add a syscall.Flock binding for Illumos and
// switch it over to use filelock_unix.go.
package filelock
import (
"errors"
"io"
"math/rand"
"os"
"sync"
"syscall"
"time"
)
type lockType int16
@ -93,7 +93,67 @@ func lock(f File, lt lockType) (err error) {
wait <- f
}
err = setlkw(f.Fd(), lt)
// Spurious EDEADLK errors arise on platforms that compute deadlock graphs at
// the process, rather than thread, level. Consider processes P and Q, with
// threads P.1, P.2, and Q.3. The following trace is NOT a deadlock, but will be
// reported as a deadlock on systems that consider only process granularity:
//
// P.1 locks file A.
// Q.3 locks file B.
// Q.3 blocks on file A.
// P.2 blocks on file B. (This is erroneously reported as a deadlock.)
// P.1 unlocks file A.
// Q.3 unblocks and locks file A.
// Q.3 unlocks files A and B.
// P.2 unblocks and locks file B.
// P.2 unlocks file B.
//
// These spurious errors were observed in practice on AIX and Solaris in
// cmd/go: see https://golang.org/issue/32817.
//
// We work around this bug by treating EDEADLK as always spurious. If there
// really is a lock-ordering bug between the interacting processes, it will
// become a livelock instead, but that's not appreciably worse than if we had
// a proper flock implementation (which generally does not even attempt to
// diagnose deadlocks).
//
// In the above example, that changes the trace to:
//
// P.1 locks file A.
// Q.3 locks file B.
// Q.3 blocks on file A.
// P.2 spuriously fails to lock file B and goes to sleep.
// P.1 unlocks file A.
// Q.3 unblocks and locks file A.
// Q.3 unlocks files A and B.
// P.2 wakes up and locks file B.
// P.2 unlocks file B.
//
// We know that the retry loop will not introduce a *spurious* livelock
// because, according to the POSIX specification, EDEADLK is only to be
// returned when “the lock is blocked by a lock from another process”.
// If that process is blocked on some lock that we are holding, then the
// resulting livelock is due to a real deadlock (and would manifest as such
// when using, for example, the flock implementation of this package).
// If the other process is *not* blocked on some other lock that we are
// holding, then it will eventually release the requested lock.
nextSleep := 1 * time.Millisecond
const maxSleep = 500 * time.Millisecond
for {
err = setlkw(f.Fd(), lt)
if err != syscall.EDEADLK {
break
}
time.Sleep(nextSleep)
nextSleep += nextSleep
if nextSleep > maxSleep {
nextSleep = maxSleep
}
// Apply 10% jitter to avoid synchronizing collisions when we finally unblock.
nextSleep += time.Duration((0.1*rand.Float64() - 0.05) * float64(nextSleep))
}
if err != nil {
unlock(f)

View file

@ -8,8 +8,11 @@
package lockedfile_test
import (
"fmt"
"internal/testenv"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"testing"
"time"
@ -172,3 +175,98 @@ func TestCanLockExistingFile(t *testing.T) {
f.Close()
wait(t)
}
// TestSpuriousEDEADLK verifies that the spurious EDEADLK reported in
// https://golang.org/issue/32817 no longer occurs.
func TestSpuriousEDEADLK(t *testing.T) {
// P.1 locks file A.
// Q.3 locks file B.
// Q.3 blocks on file A.
// P.2 blocks on file B. (Spurious EDEADLK occurs here.)
// P.1 unlocks file A.
// Q.3 unblocks and locks file A.
// Q.3 unlocks files A and B.
// P.2 unblocks and locks file B.
// P.2 unlocks file B.
testenv.MustHaveExec(t)
dirVar := t.Name() + "DIR"
if dir := os.Getenv(dirVar); dir != "" {
// Q.3 locks file B.
b, err := lockedfile.Edit(filepath.Join(dir, "B"))
if err != nil {
t.Fatal(err)
}
defer b.Close()
if err := ioutil.WriteFile(filepath.Join(dir, "locked"), []byte("ok"), 0666); err != nil {
t.Fatal(err)
}
// Q.3 blocks on file A.
a, err := lockedfile.Edit(filepath.Join(dir, "A"))
// Q.3 unblocks and locks file A.
if err != nil {
t.Fatal(err)
}
defer a.Close()
// Q.3 unlocks files A and B.
return
}
dir, remove := mustTempDir(t)
defer remove()
// P.1 locks file A.
a, err := lockedfile.Edit(filepath.Join(dir, "A"))
if err != nil {
t.Fatal(err)
}
cmd := exec.Command(os.Args[0], "-test.run="+t.Name())
cmd.Env = append(os.Environ(), fmt.Sprintf("%s=%s", dirVar, dir))
qDone := make(chan struct{})
waitQ := mustBlock(t, "Edit A and B in subprocess", func() {
out, err := cmd.CombinedOutput()
if err != nil {
t.Errorf("%v:\n%s", err, out)
}
close(qDone)
})
// Wait until process Q has either failed or locked file B.
// Otherwise, P.2 might not block on file B as intended.
locked:
for {
if _, err := os.Stat(filepath.Join(dir, "locked")); !os.IsNotExist(err) {
break locked
}
select {
case <-qDone:
break locked
case <-time.After(1 * time.Millisecond):
}
}
waitP2 := mustBlock(t, "Edit B", func() {
// P.2 blocks on file B. (Spurious EDEADLK occurs here.)
b, err := lockedfile.Edit(filepath.Join(dir, "B"))
// P.2 unblocks and locks file B.
if err != nil {
t.Error(err)
return
}
// P.2 unlocks file B.
b.Close()
})
// P.1 unlocks file A.
a.Close()
waitQ(t)
waitP2(t)
}

View file

@ -24,7 +24,8 @@ var cmdDownload = &base.Command{
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.
With no arguments, download applies to all dependencies of the main module
(equivalent to 'go mod download all').
The go command will automatically download modules as needed during ordinary
execution. The "go mod download" command is useful mainly for pre-filling

View file

@ -123,6 +123,11 @@ func runVendor(cmd *base.Command, args []string) {
fmt.Fprintf(os.Stderr, "go: no dependencies to vendor\n")
return
}
if err := os.MkdirAll(vdir, 0777); err != nil {
base.Fatalf("go mod vendor: %v", err)
}
if err := ioutil.WriteFile(filepath.Join(vdir, "modules.txt"), buf.Bytes(), 0666); err != nil {
base.Fatalf("go mod vendor: %v", err)
}

View file

@ -10,6 +10,7 @@ import (
"fmt"
"io/ioutil"
"os"
"runtime"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
@ -52,17 +53,41 @@ func runVerify(cmd *base.Command, args []string) {
base.Fatalf("go: cannot find main module; see 'go help modules'")
}
}
// Only verify up to GOMAXPROCS zips at once.
type token struct{}
sem := make(chan token, runtime.GOMAXPROCS(0))
// Use a slice of result channels, so that the output is deterministic.
mods := modload.LoadBuildList()[1:]
errsChans := make([]<-chan []error, len(mods))
for i, mod := range mods {
sem <- token{}
errsc := make(chan []error, 1)
errsChans[i] = errsc
mod := mod // use a copy to avoid data races
go func() {
errsc <- verifyMod(mod)
<-sem
}()
}
ok := true
for _, mod := range modload.LoadBuildList()[1:] {
ok = verifyMod(mod) && ok
for _, errsc := range errsChans {
errs := <-errsc
for _, err := range errs {
base.Errorf("%s", err)
ok = false
}
}
if ok {
fmt.Printf("all modules verified\n")
}
}
func verifyMod(mod module.Version) bool {
ok := true
func verifyMod(mod module.Version) []error {
var errs []error
zip, zipErr := modfetch.CachePath(mod, "zip")
if zipErr == nil {
_, zipErr = os.Stat(zip)
@ -73,10 +98,10 @@ func verifyMod(mod module.Version) bool {
if zipErr != nil && errors.Is(zipErr, os.ErrNotExist) &&
dirErr != nil && errors.Is(dirErr, os.ErrNotExist) {
// Nothing downloaded yet. Nothing to verify.
return true
return nil
}
base.Errorf("%s %s: missing ziphash: %v", mod.Path, mod.Version, err)
return false
errs = append(errs, fmt.Errorf("%s %s: missing ziphash: %v", mod.Path, mod.Version, err))
return errs
}
h := string(bytes.TrimSpace(data))
@ -85,11 +110,10 @@ func verifyMod(mod module.Version) bool {
} else {
hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash)
if err != nil {
base.Errorf("%s %s: %v", mod.Path, mod.Version, err)
return false
errs = append(errs, fmt.Errorf("%s %s: %v", mod.Path, mod.Version, err))
return errs
} else if hZ != h {
base.Errorf("%s %s: zip has been modified (%v)", mod.Path, mod.Version, zip)
ok = false
errs = append(errs, fmt.Errorf("%s %s: zip has been modified (%v)", mod.Path, mod.Version, zip))
}
}
if dirErr != nil && errors.Is(dirErr, os.ErrNotExist) {
@ -98,13 +122,12 @@ func verifyMod(mod module.Version) bool {
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
errs = append(errs, fmt.Errorf("%s %s: %v", mod.Path, mod.Version, err))
return errs
}
if hD != h {
base.Errorf("%s %s: dir has been modified (%v)", mod.Path, mod.Version, dir)
ok = false
errs = append(errs, fmt.Errorf("%s %s: dir has been modified (%v)", mod.Path, mod.Version, dir))
}
}
return ok
return errs
}

View file

@ -18,7 +18,6 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/modfetch"
"cmd/go/internal/modfetch/codehost"
"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
@ -42,8 +41,7 @@ func testMain(m *testing.M) int {
log.Fatal(err)
}
defer os.RemoveAll(dir)
modfetch.PkgMod = filepath.Join(dir, "pkg/mod")
codehost.WorkRoot = filepath.Join(dir, "codework")
cfg.GOMODCACHE = filepath.Join(dir, "pkg/mod")
return m.Run()
}

View file

@ -26,17 +26,17 @@ import (
"golang.org/x/mod/semver"
)
var PkgMod string // $GOPATH/pkg/mod; set by package modload
func cacheDir(path string) (string, error) {
if PkgMod == "" {
return "", fmt.Errorf("internal error: modfetch.PkgMod not set")
if cfg.GOMODCACHE == "" {
// modload.Init exits if GOPATH[0] is empty, and cfg.GOMODCACHE
// is set to GOPATH[0]/pkg/mod if GOMODCACHE is empty, so this should never happen.
return "", fmt.Errorf("internal error: cfg.GOMODCACHE not set")
}
enc, err := module.EscapePath(path)
if err != nil {
return "", err
}
return filepath.Join(PkgMod, "cache/download", enc, "/@v"), nil
return filepath.Join(cfg.GOMODCACHE, "cache/download", enc, "/@v"), nil
}
func CachePath(m module.Version, suffix string) (string, error) {
@ -63,8 +63,10 @@ func CachePath(m module.Version, suffix string) (string, error) {
// along with the directory if the directory does not exist or if the directory
// is not completely populated.
func DownloadDir(m module.Version) (string, error) {
if PkgMod == "" {
return "", fmt.Errorf("internal error: modfetch.PkgMod not set")
if cfg.GOMODCACHE == "" {
// modload.Init exits if GOPATH[0] is empty, and cfg.GOMODCACHE
// is set to GOPATH[0]/pkg/mod if GOMODCACHE is empty, so this should never happen.
return "", fmt.Errorf("internal error: cfg.GOMODCACHE not set")
}
enc, err := module.EscapePath(m.Path)
if err != nil {
@ -81,7 +83,7 @@ func DownloadDir(m module.Version) (string, error) {
return "", err
}
dir := filepath.Join(PkgMod, enc+"@"+encVer)
dir := filepath.Join(cfg.GOMODCACHE, enc+"@"+encVer)
if fi, err := os.Stat(dir); os.IsNotExist(err) {
return dir, err
} else if err != nil {
@ -131,11 +133,13 @@ func lockVersion(mod module.Version) (unlock func(), err error) {
// user's working directory.
// If err is nil, the caller MUST eventually call the unlock function.
func SideLock() (unlock func(), err error) {
if PkgMod == "" {
base.Fatalf("go: internal error: modfetch.PkgMod not set")
if cfg.GOMODCACHE == "" {
// modload.Init exits if GOPATH[0] is empty, and cfg.GOMODCACHE
// is set to GOPATH[0]/pkg/mod if GOMODCACHE is empty, so this should never happen.
base.Fatalf("go: internal error: cfg.GOMODCACHE not set")
}
path := filepath.Join(PkgMod, "cache", "lock")
path := filepath.Join(cfg.GOMODCACHE, "cache", "lock")
if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
return nil, fmt.Errorf("failed to create cache directory: %w", err)
}
@ -456,7 +460,7 @@ func readDiskStat(path, rev string) (file string, info *RevInfo, err error) {
// just to find out about a commit we already know about
// (and have cached under its pseudo-version).
func readDiskStatByHash(path, rev string) (file string, info *RevInfo, err error) {
if PkgMod == "" {
if cfg.GOMODCACHE == "" {
// Do not download to current directory.
return "", nil, errNotCached
}

View file

@ -153,15 +153,11 @@ func ShortenSHA1(rev string) string {
return rev
}
// WorkRoot is the root of the cached work directory.
// It is set by cmd/go/internal/modload.InitMod.
var WorkRoot string
// WorkDir returns the name of the cached work directory to use for the
// given repository type and name.
func WorkDir(typ, name string) (dir, lockfile string, err error) {
if WorkRoot == "" {
return "", "", fmt.Errorf("codehost.WorkRoot not set")
if cfg.GOMODCACHE == "" {
return "", "", fmt.Errorf("neither GOPATH nor GOMODCACHE are set")
}
// We name the work directory for the SHA256 hash of the type and name.
@ -173,7 +169,7 @@ func WorkDir(typ, name string) (dir, lockfile string, err error) {
return "", "", fmt.Errorf("codehost.WorkDir: type cannot contain colon")
}
key := typ + ":" + name
dir = filepath.Join(WorkRoot, fmt.Sprintf("%x", sha256.Sum256([]byte(key))))
dir = filepath.Join(cfg.GOMODCACHE, "cache/vcs", fmt.Sprintf("%x", sha256.Sum256([]byte(key))))
if cfg.BuildX {
fmt.Fprintf(os.Stderr, "mkdir -p %s # %s %s\n", filepath.Dir(dir), typ, name)

View file

@ -27,11 +27,6 @@ import (
"golang.org/x/mod/semver"
)
// GitRepo returns the code repository at the given Git remote reference.
func GitRepo(remote string) (Repo, error) {
return newGitRepoCached(remote, false)
}
// LocalGitRepo is like Repo but accepts both Git remote references
// and paths to repositories on the local file system.
func LocalGitRepo(remote string) (Repo, error) {

View file

@ -57,7 +57,6 @@ func testMain(m *testing.M) int {
log.Fatal(err)
}
defer os.RemoveAll(dir)
WorkRoot = dir
if testenv.HasExternalNetwork() && testenv.HasExec() {
// Clone gitrepo1 into a local directory.

View file

@ -20,6 +20,7 @@ import (
"strings"
"time"
"cmd/go/internal/cfg"
"cmd/go/internal/modfetch/codehost"
)
@ -29,7 +30,7 @@ func usage() {
}
func main() {
codehost.WorkRoot = "/tmp/vcswork"
cfg.GOMODCACHE = "/tmp/vcswork"
log.SetFlags(0)
log.SetPrefix("shell: ")
flag.Usage = usage

View file

@ -563,7 +563,7 @@ func (r *codeRepo) validatePseudoVersion(info *codehost.RevInfo, version string)
return err
}
if !t.Equal(info.Time.Truncate(time.Second)) {
return fmt.Errorf("does not match version-control timestamp (%s)", info.Time.UTC().Format(time.RFC3339))
return fmt.Errorf("does not match version-control timestamp (expected %s)", info.Time.UTC().Format(pseudoVersionTimestampFormat))
}
tagPrefix := ""
@ -1012,28 +1012,3 @@ func hasPathPrefix(s, prefix string) bool {
return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
}
}
func isVendoredPackage(name string) bool {
var i int
if strings.HasPrefix(name, "vendor/") {
i += len("vendor/")
} else if j := strings.Index(name, "/vendor/"); j >= 0 {
// This offset looks incorrect; this should probably be
//
// i = j + len("/vendor/")
//
// (See https://golang.org/issue/31562.)
//
// Unfortunately, we can't fix it without invalidating checksums.
// Fortunately, the error appears to be strictly conservative: we'll retain
// vendored packages that we should have pruned, but we won't prune
// non-vendored packages that we should have retained.
//
// Since this defect doesn't seem to break anything, it's not worth fixing
// for now.
i += len("/vendor/")
} else {
return false
}
return strings.Contains(name[i:], "/")
}

View file

@ -44,7 +44,7 @@ func testMain(m *testing.M) int {
}
defer os.RemoveAll(dir)
codehost.WorkRoot = dir
cfg.GOMODCACHE = dir
return m.Run()
}

View file

@ -35,9 +35,10 @@ var downloadCache par.Cache
// local download cache and returns the name of the directory
// corresponding to the root of the module's file tree.
func Download(mod module.Version) (dir string, err error) {
if PkgMod == "" {
// Do not download to current directory.
return "", fmt.Errorf("missing modfetch.PkgMod")
if cfg.GOMODCACHE == "" {
// modload.Init exits if GOPATH[0] is empty, and cfg.GOMODCACHE
// is set to GOPATH[0]/pkg/mod if GOMODCACHE is empty, so this should never happen.
base.Fatalf("go: internal error: cfg.GOMODCACHE not set")
}
// The par.Cache here avoids duplicate work.
@ -57,11 +58,10 @@ func Download(mod module.Version) (dir string, err error) {
}
func download(mod module.Version) (dir string, err error) {
// If the directory exists, and no .partial file exists,
// the module has already been completely extracted.
// .partial files may be created when future versions of cmd/go
// extract module zip directories in place instead of extracting
// to a random temporary directory and renaming.
// If the directory exists, and no .partial file exists, the module has
// already been completely extracted. .partial files may be created when a
// module zip directory is extracted in place instead of being extracted to a
// temporary directory and renamed.
dir, err = DownloadDir(mod)
if err == nil {
return dir, nil
@ -115,31 +115,61 @@ func download(mod module.Version) (dir string, err error) {
return "", err
}
// Extract the zip file to a temporary directory, then rename it to the
// final path. That way, we can use the existence of the source directory to
// signal that it has been extracted successfully, and if someone deletes
// the entire directory (e.g. as an attempt to prune out file corruption)
// the module cache will still be left in a recoverable state.
// Extract the module zip directory.
//
// By default, we extract to a temporary directory, then atomically rename to
// its final location. We use the existence of the source directory to signal
// that it has been extracted successfully (see DownloadDir). If someone
// deletes the entire directory (e.g., as an attempt to prune out file
// corruption), the module cache will still be left in a recoverable
// state.
//
// Unfortunately, os.Rename may fail with ERROR_ACCESS_DENIED on Windows if
// another process opens files in the temporary directory. This is partially
// mitigated by using robustio.Rename, which retries os.Rename for a short
// time.
//
// To avoid this error completely, if unzipInPlace is set, we instead create a
// .partial file (indicating the directory isn't fully extracted), then we
// extract the directory at its final location, then we delete the .partial
// file. This is not the default behavior because older versions of Go may
// simply stat the directory to check whether it exists without looking for a
// .partial file. If multiple versions run concurrently, the older version may
// assume a partially extracted directory is complete.
// TODO(golang.org/issue/36568): when these older versions are no longer
// supported, remove the old default behavior and the unzipInPlace flag.
if err := os.MkdirAll(parentDir, 0777); err != nil {
return "", err
}
tmpDir, err := ioutil.TempDir(parentDir, tmpPrefix)
if err != nil {
return "", err
}
defer func() {
if err != nil {
RemoveAll(tmpDir)
if unzipInPlace {
if err := ioutil.WriteFile(partialPath, nil, 0666); err != nil {
return "", err
}
if err := modzip.Unzip(dir, mod, zipfile); err != nil {
fmt.Fprintf(os.Stderr, "-> %s\n", err)
if rmErr := RemoveAll(dir); rmErr == nil {
os.Remove(partialPath)
}
return "", err
}
if err := os.Remove(partialPath); err != nil {
return "", err
}
} else {
tmpDir, err := ioutil.TempDir(parentDir, tmpPrefix)
if err != nil {
return "", err
}
if err := modzip.Unzip(tmpDir, mod, zipfile); err != nil {
fmt.Fprintf(os.Stderr, "-> %s\n", err)
RemoveAll(tmpDir)
return "", err
}
if err := robustio.Rename(tmpDir, dir); err != nil {
RemoveAll(tmpDir)
return "", err
}
}()
if err := modzip.Unzip(tmpDir, mod, zipfile); err != nil {
fmt.Fprintf(os.Stderr, "-> %s\n", err)
return "", err
}
if err := robustio.Rename(tmpDir, dir); err != nil {
return "", err
}
if !cfg.ModCacheRW {
@ -150,6 +180,17 @@ func download(mod module.Version) (dir string, err error) {
return dir, nil
}
var unzipInPlace bool
func init() {
for _, f := range strings.Split(os.Getenv("GODEBUG"), ",") {
if f == "modcacheunzipinplace=1" {
unzipInPlace = true
break
}
}
}
var downloadZipCache par.Cache
// DownloadZip downloads the specific module version to the
@ -321,7 +362,7 @@ func RemoveAll(dir string) error {
}
return nil
})
return os.RemoveAll(dir)
return robustio.RemoveAll(dir)
}
var GoSumFile string // path to go.sum; set by package modload
@ -416,7 +457,7 @@ func readGoSum(dst map[module.Version][]string, file string, data []byte) error
// checkMod checks the given module's checksum.
func checkMod(mod module.Version) {
if PkgMod == "" {
if cfg.GOMODCACHE == "" {
// Do not use current directory.
return
}
@ -473,6 +514,7 @@ func checkModSum(mod module.Version, h string) error {
goSum.mu.Lock()
inited, err := initGoSum()
if err != nil {
goSum.mu.Unlock()
return err
}
done := inited && haveModSumLocked(mod, h)
@ -553,7 +595,7 @@ func checkSumDB(mod module.Version, h string) error {
// Sum returns the checksum for the downloaded copy of the given module,
// if present in the download cache.
func Sum(mod module.Version) string {
if PkgMod == "" {
if cfg.GOMODCACHE == "" {
// Do not use current directory.
return ""
}

View file

@ -101,27 +101,51 @@ cached module versions with GOPROXY=https://example.com/proxy.
var proxyOnce struct {
sync.Once
list []string
list []proxySpec
err error
}
func proxyURLs() ([]string, error) {
type proxySpec struct {
// url is the proxy URL or one of "off", "direct", "noproxy".
url string
// fallBackOnError is true if a request should be attempted on the next proxy
// in the list after any error from this proxy. If fallBackOnError is false,
// the request will only be attempted on the next proxy if the error is
// equivalent to os.ErrNotFound, which is true for 404 and 410 responses.
fallBackOnError bool
}
func proxyList() ([]proxySpec, error) {
proxyOnce.Do(func() {
if cfg.GONOPROXY != "" && cfg.GOPROXY != "direct" {
proxyOnce.list = append(proxyOnce.list, "noproxy")
proxyOnce.list = append(proxyOnce.list, proxySpec{url: "noproxy"})
}
for _, proxyURL := range strings.Split(cfg.GOPROXY, ",") {
proxyURL = strings.TrimSpace(proxyURL)
if proxyURL == "" {
goproxy := cfg.GOPROXY
for goproxy != "" {
var url string
fallBackOnError := false
if i := strings.IndexAny(goproxy, ",|"); i >= 0 {
url = goproxy[:i]
fallBackOnError = goproxy[i] == '|'
goproxy = goproxy[i+1:]
} else {
url = goproxy
goproxy = ""
}
url = strings.TrimSpace(url)
if url == "" {
continue
}
if proxyURL == "off" {
if url == "off" {
// "off" always fails hard, so can stop walking list.
proxyOnce.list = append(proxyOnce.list, "off")
proxyOnce.list = append(proxyOnce.list, proxySpec{url: "off"})
break
}
if proxyURL == "direct" {
proxyOnce.list = append(proxyOnce.list, "direct")
if url == "direct" {
proxyOnce.list = append(proxyOnce.list, proxySpec{url: "direct"})
// For now, "direct" is the end of the line. We may decide to add some
// sort of fallback behavior for them in the future, so ignore
// subsequent entries for forward-compatibility.
@ -131,18 +155,29 @@ func proxyURLs() ([]string, error) {
// Single-word tokens are reserved for built-in behaviors, and anything
// containing the string ":/" or matching an absolute file path must be a
// complete URL. For all other paths, implicitly add "https://".
if strings.ContainsAny(proxyURL, ".:/") && !strings.Contains(proxyURL, ":/") && !filepath.IsAbs(proxyURL) && !path.IsAbs(proxyURL) {
proxyURL = "https://" + proxyURL
if strings.ContainsAny(url, ".:/") && !strings.Contains(url, ":/") && !filepath.IsAbs(url) && !path.IsAbs(url) {
url = "https://" + url
}
// Check that newProxyRepo accepts the URL.
// It won't do anything with the path.
_, err := newProxyRepo(proxyURL, "golang.org/x/text")
if err != nil {
if _, err := newProxyRepo(url, "golang.org/x/text"); err != nil {
proxyOnce.err = err
return
}
proxyOnce.list = append(proxyOnce.list, proxyURL)
proxyOnce.list = append(proxyOnce.list, proxySpec{
url: url,
fallBackOnError: fallBackOnError,
})
}
if len(proxyOnce.list) == 0 ||
len(proxyOnce.list) == 1 && proxyOnce.list[0].url == "noproxy" {
// There were no proxies, other than the implicit "noproxy" added when
// GONOPROXY is set. This can happen if GOPROXY is a non-empty string
// like "," or " ".
proxyOnce.err = fmt.Errorf("GOPROXY list is not the empty string, but contains no entries")
}
})
@ -150,44 +185,60 @@ func proxyURLs() ([]string, error) {
}
// TryProxies iterates f over each configured proxy (including "noproxy" and
// "direct" if applicable) until f returns an error that is not
// equivalent to os.ErrNotExist.
// "direct" if applicable) until f returns no error or until f returns an
// error that is not equivalent to os.ErrNotExist on a proxy configured
// not to fall back on errors.
//
// TryProxies then returns that final error.
//
// If GOPROXY is set to "off", TryProxies invokes f once with the argument
// "off".
func TryProxies(f func(proxy string) error) error {
proxies, err := proxyURLs()
proxies, err := proxyList()
if err != nil {
return err
}
if len(proxies) == 0 {
return f("off")
panic("GOPROXY list is empty")
}
var lastAttemptErr error
// We try to report the most helpful error to the user. "direct" and "noproxy"
// errors are best, followed by proxy errors other than ErrNotExist, followed
// by ErrNotExist.
//
// Note that errProxyOff, errNoproxy, and errUseProxy are equivalent to
// ErrNotExist. errUseProxy should only be returned if "noproxy" is the only
// proxy. errNoproxy should never be returned, since there should always be a
// more useful error from "noproxy" first.
const (
notExistRank = iota
proxyRank
directRank
)
var bestErr error
bestErrRank := notExistRank
for _, proxy := range proxies {
err = f(proxy)
if !errors.Is(err, os.ErrNotExist) {
lastAttemptErr = err
err := f(proxy.url)
if err == nil {
return nil
}
isNotExistErr := errors.Is(err, os.ErrNotExist)
if proxy.url == "direct" || (proxy.url == "noproxy" && err != errUseProxy) {
bestErr = err
bestErrRank = directRank
} else if bestErrRank <= proxyRank && !isNotExistErr {
bestErr = err
bestErrRank = proxyRank
} else if bestErrRank == notExistRank {
bestErr = err
}
if !proxy.fallBackOnError && !isNotExistErr {
break
}
// The error indicates that the module does not exist.
// In general we prefer to report the last such error,
// because it indicates the error that occurs after all other
// options have been exhausted.
//
// However, for modules in the NOPROXY list, the most useful error occurs
// first (with proxy set to "noproxy"), and the subsequent errors are all
// errNoProxy (which is not particularly helpful). Do not overwrite a more
// useful error with errNoproxy.
if lastAttemptErr == nil || !errors.Is(err, errNoproxy) {
lastAttemptErr = err
}
}
return lastAttemptErr
return bestErr
}
type proxyRepo struct {
@ -205,12 +256,12 @@ func newProxyRepo(baseURL, path string) (Repo, error) {
// ok
case "file":
if *base != (url.URL{Scheme: base.Scheme, Path: base.Path, RawPath: base.RawPath}) {
return nil, fmt.Errorf("invalid file:// proxy URL with non-path elements: %s", web.Redacted(base))
return nil, fmt.Errorf("invalid file:// proxy URL with non-path elements: %s", base.Redacted())
}
case "":
return nil, fmt.Errorf("invalid proxy URL missing scheme: %s", web.Redacted(base))
return nil, fmt.Errorf("invalid proxy URL missing scheme: %s", base.Redacted())
default:
return nil, fmt.Errorf("invalid proxy URL scheme (must be https, http, file): %s", web.Redacted(base))
return nil, fmt.Errorf("invalid proxy URL scheme (must be https, http, file): %s", base.Redacted())
}
enc, err := module.EscapePath(path)

View file

@ -48,6 +48,8 @@ import (
var pseudoVersionRE = lazyregexp.New(`^v[0-9]+\.(0\.0-|\d+\.\d+-([^+]*\.)?0\.)\d{14}-[A-Za-z0-9]+(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$`)
const pseudoVersionTimestampFormat = "20060102150405"
// PseudoVersion returns a pseudo-version for the given major version ("v1")
// preexisting older tagged version ("" or "v1.2.3" or "v1.2.3-pre"), revision time,
// and revision identifier (usually a 12-byte commit hash prefix).
@ -55,7 +57,7 @@ func PseudoVersion(major, older string, t time.Time, rev string) string {
if major == "" {
major = "v0"
}
segment := fmt.Sprintf("%s-%s", t.UTC().Format("20060102150405"), rev)
segment := fmt.Sprintf("%s-%s", t.UTC().Format(pseudoVersionTimestampFormat), rev)
build := semver.Build(older)
older = semver.Canonical(older)
if older == "" {

View file

@ -26,6 +26,7 @@ import (
"cmd/go/internal/lockedfile"
"cmd/go/internal/str"
"cmd/go/internal/web"
"golang.org/x/mod/module"
"golang.org/x/mod/sumdb"
"golang.org/x/mod/sumdb/note"
@ -130,7 +131,7 @@ func (c *dbClient) ReadRemote(path string) ([]byte, error) {
targ := web.Join(c.base, path)
data, err := web.GetBytes(targ)
if false {
fmt.Fprintf(os.Stderr, "%.3fs %s\n", time.Since(start).Seconds(), web.Redacted(targ))
fmt.Fprintf(os.Stderr, "%.3fs %s\n", time.Since(start).Seconds(), targ.Redacted())
}
return data, err
}
@ -146,49 +147,50 @@ func (c *dbClient) initBase() {
}
// Try proxies in turn until we find out how to connect to this database.
urls, err := proxyURLs()
if err != nil {
c.baseErr = err
return
}
for _, proxyURL := range urls {
if proxyURL == "noproxy" {
continue
}
if proxyURL == "direct" || proxyURL == "off" {
break
}
proxy, err := url.Parse(proxyURL)
if err != nil {
c.baseErr = err
return
}
// Quoting https://golang.org/design/25530-sumdb#proxying-a-checksum-database:
//
// Before accessing any checksum database URL using a proxy,
// the proxy client should first fetch <proxyURL>/sumdb/<sumdb-name>/supported.
// If that request returns a successful (HTTP 200) response, then the proxy supports
// proxying checksum database requests. In that case, the client should use
// the proxied access method only, never falling back to a direct connection to the database.
// If the /sumdb/<sumdb-name>/supported check fails with a “not found” (HTTP 404)
// or “gone” (HTTP 410) response, the proxy is unwilling to proxy the checksum database,
// and the client should connect directly to the database.
// Any other response is treated as the database being unavailable.
_, err = web.GetBytes(web.Join(proxy, "sumdb/"+c.name+"/supported"))
if err == nil {
//
// Before accessing any checksum database URL using a proxy, the proxy
// client should first fetch <proxyURL>/sumdb/<sumdb-name>/supported.
//
// If that request returns a successful (HTTP 200) response, then the proxy
// supports proxying checksum database requests. In that case, the client
// should use the proxied access method only, never falling back to a direct
// connection to the database.
//
// If the /sumdb/<sumdb-name>/supported check fails with a “not found” (HTTP
// 404) or “gone” (HTTP 410) response, or if the proxy is configured to fall
// back on errors, the client will try the next proxy. If there are no
// proxies left or if the proxy is "direct" or "off", the client should
// connect directly to that database.
//
// Any other response is treated as the database being unavailable.
//
// See https://golang.org/design/25530-sumdb#proxying-a-checksum-database.
err := TryProxies(func(proxy string) error {
switch proxy {
case "noproxy":
return errUseProxy
case "direct", "off":
return errProxyOff
default:
proxyURL, err := url.Parse(proxy)
if err != nil {
return err
}
if _, err := web.GetBytes(web.Join(proxyURL, "sumdb/"+c.name+"/supported")); err != nil {
return err
}
// Success! This proxy will help us.
c.base = web.Join(proxy, "sumdb/"+c.name)
return
}
// If the proxy serves a non-404/410, give up.
if !errors.Is(err, os.ErrNotExist) {
c.baseErr = err
return
c.base = web.Join(proxyURL, "sumdb/"+c.name)
return nil
}
})
if errors.Is(err, os.ErrNotExist) {
// No proxies, or all proxies failed (with 404, 410, or were were allowed
// to fall back), or we reached an explicit "direct" or "off".
c.base = c.direct
} else if err != nil {
c.baseErr = err
}
// No proxies, or all proxies said 404, or we reached an explicit "direct".
c.base = c.direct
}
// ReadConfig reads the key from c.key
@ -198,8 +200,10 @@ func (c *dbClient) ReadConfig(file string) (data []byte, err error) {
return []byte(c.key), nil
}
// GOPATH/pkg is PkgMod/..
targ := filepath.Join(PkgMod, "../sumdb/"+file)
if cfg.SumdbDir == "" {
return nil, errors.New("could not locate sumdb file: missing $GOPATH")
}
targ := filepath.Join(cfg.SumdbDir, file)
data, err = lockedfile.Read(targ)
if errors.Is(err, os.ErrNotExist) {
// Treat non-existent as empty, to bootstrap the "latest" file
@ -215,7 +219,10 @@ func (*dbClient) WriteConfig(file string, old, new []byte) error {
// Should not happen.
return fmt.Errorf("cannot write key")
}
targ := filepath.Join(PkgMod, "../sumdb/"+file)
if cfg.SumdbDir == "" {
return errors.New("could not locate sumdb file: missing $GOPATH")
}
targ := filepath.Join(cfg.SumdbDir, file)
os.MkdirAll(filepath.Dir(targ), 0777)
f, err := lockedfile.Edit(targ)
if err != nil {
@ -245,7 +252,7 @@ func (*dbClient) WriteConfig(file string, old, new []byte) error {
// GOPATH/pkg/mod/cache/download/sumdb,
// which will be deleted by "go clean -modcache".
func (*dbClient) ReadCache(file string) ([]byte, error) {
targ := filepath.Join(PkgMod, "cache/download/sumdb", file)
targ := filepath.Join(cfg.GOMODCACHE, "cache/download/sumdb", file)
data, err := lockedfile.Read(targ)
// lockedfile.Write does not atomically create the file with contents.
// There is a moment between file creation and locking the file for writing,
@ -259,7 +266,7 @@ func (*dbClient) ReadCache(file string) ([]byte, error) {
// WriteCache updates cached lookups or tiles.
func (*dbClient) WriteCache(file string, data []byte) {
targ := filepath.Join(PkgMod, "cache/download/sumdb", file)
targ := filepath.Join(cfg.GOMODCACHE, "cache/download/sumdb", file)
os.MkdirAll(filepath.Dir(targ), 0777)
lockedfile.Write(targ, bytes.NewReader(data), 0666)
}

View file

@ -40,7 +40,6 @@ git.torproject.org/pluggable-transports/goptlib.git,v1.1.0,h1:LMQAA8pAho+QtYrrVN
gitee.com/nggs/util,v0.0.0-20190830024003-3e49d2efc84b,h1:6KQpPEs326uPrICQy9x/PxmR8U0v/XsFzpt0k1nFKcY=,a062c99c2b560a36168fe51eab8f17f4fadf5d534238881628e83d8d61e51c2a
github.com/1and1/oneandone-cloudserver-sdk-go,v1.0.1,h1:RMTyvS5bjvSWiUcfqfr/E2pxHEMrALvU+E12n6biymg=,7f068808fc0857d7de8c8f829cc380dce1c6611a3fc819daf4421e9bcb75a07c
github.com/99designs/gqlgen,v0.10.1,h1:1BgB6XKGTHq7uH4G1/PYyKe2Kz7/vw3AlvMZlD3TEEY=,04b9e7d8a3df6543cd870325b1140ce9ac3f4bbfd8c90ebecec4f908dd420d08
github.com/AlexStocks/log4go,v1.0.5,h1:45boeHy0qh0NFBaEhrFT/pUKzQUGf7q2Ux1iQDr/f6o=,59371c2108f62aa9a2233ca8f7de57868ad2c64313b2d68434e0ed6a1748ce2c
github.com/AndreasBriese/bbloom,v0.0.0-20190306092124-e2d15f34fcf9,h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4=,6d7c1af06f8597fde1e86166f26416057392f1b0bdb84f2af555aa461282dd18
github.com/AsynkronIT/goconsole,v0.0.0-20160504192649-bfa12eebf716,h1:Pk/Kzi5O0T4QxfqvbaUsh8UklbJ9BklZ/ClZBptX5WU=,5a2507b89bb4436881718d785a0ef383652aa99782508b7444cf20255082dab9
github.com/Azure/azure-amqp-common-go,v1.1.4,h1:DmPXxmLZwi/71CgRTZIKR6yiKEW3eC42S4gSBhfG7y0=,4b800793ff4fefa86a427c445e3a4671b8d1dcd87a44075f6309cace6b0e01e2
@ -243,7 +242,6 @@ github.com/beego/x2j,v0.0.0-20131220205130-a0352aadc542,h1:nYXb+3jF6Oq/j8R/y90Xr
github.com/beevik/etree,v1.1.0,h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=,614a33736f8b9262a809f101df5bf71f47777879b1191165b6247d6b67c7468c
github.com/beevik/guid,v0.0.0-20170504223318-d0ea8faecee0,h1:oLd/YLOTOgA4D4aAUhIE8vhl/LAP1ZJrj0mDQpl7GB8=,5add94fcade6c7afa236112c8da300d47ec499ad1789a5e805c8198062dd0749
github.com/beevik/ntp,v0.2.0,h1:sGsd+kAXzT0bfVfzJfce04g+dSRfrs+tbQW8lweuYgw=,42e14f30c23ba2f5ddaff76101016d87f0f0a0f1d96d3d20e42fd02842091c76
github.com/belogik/goes,v0.0.0-20151229125003-e54d722c3aff,h1:/kO0p2RTGLB8R5gub7ps0GmYpB2O8LXEoPq8tzFDCUI=,f926f1040febe5318efa145541a6fc7898d32514bc13899e812185f05710c5db
github.com/beorn7/perks,v1.0.1,h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=,25bd9e2d94aca770e6dbc1f53725f84f6af4432f631d35dd2c46f96ef0512f1a
github.com/bep/debounce,v1.2.0,h1:wXds8Kq8qRfwAOpAxHrJDbCXgC5aHSzgQb/0gKsHQqo=,ddc0a77e4819b6b826d69fdf1a5a153f3f867a31e030cfe28296355b670adf21
github.com/bep/gitmap,v1.1.1,h1:Nf8ySnC3I7/xPjuWeCwzukUFv185iTUQ6nOvLy9gCJA=,364163e67741ae331d164fd881964160f19fdbdfe094e0e762314cc37aac646a
@ -1461,7 +1459,6 @@ github.com/opencontainers/runc,v0.1.1,h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJ
github.com/opencontainers/runtime-spec,v1.0.1,h1:wY4pOY8fBdSIvs9+IDHC55thBuEulhzfSgKeC1yFvzQ=,1958458b00ce912425f5c7d2ee836431b296a3f9320d565512d8c96b107fffbf
github.com/opencontainers/runtime-tools,v0.9.0,h1:FYgwVsKRI/H9hU32MJ/4MLOzXWodKK5zsQavY8NPMkU=,53c720dbb7452cfb2fd3945e37c26b5a0140cb1012d35a2b72a5e035f28a32c4
github.com/opencontainers/selinux,v1.3.0,h1:xsI95WzPZu5exzA6JzkLSfdr/DilzOhCJOqGe5TgR0g=,88286825b32cd46a0469e578f378a185032da2d5b03893623861ef3af59359d8
github.com/openshift/api,v3.9.0+incompatible,h1:fJ/KsefYuZAjmrr3+5U9yZIZbTOpVkDDLDLFresAeYs=,fc087ac9809ce58bdd15614e04c13f8ecc4a17e71addbe6eb6b777c377b01243
github.com/openshift/client-go,v3.9.0+incompatible,h1:13k3Ok0B7TA2hA3bQW2aFqn6y04JaJWdk7ITTyg+Ek0=,661b7f28b4905f1936dd58e373374513d54663ec85aecafede1c7d9c260e9369
github.com/openshift/library-go,v0.0.0-20191101161407-e7c97b468b83,h1:wwR+laNaFKVGiizoIDL/cAKIZVoKXJ9jbjUoUlq2p5I=,c74f8134013f978ef154d6accf9b4b0c5126941f2d45e6eb223db7098f7ab2a4
github.com/opentracing-contrib/go-observer,v0.0.0-20170622124052-a52f23424492,h1:lM6RxxfUMrYL/f8bWEUqdXrANWtrL7Nndbm9iFN0DlU=,50023eee1ef04412410f43d8b5dcf3ef481c0fc39067add27799654705fa84b2
@ -1882,7 +1879,6 @@ github.com/zondax/ledger-go,v0.9.0,h1:oTrtFqPFA4VdCPRvqMaN45mQnJxkPc0JxoVZfCoUpj
github.com/zquestz/grab,v0.0.0-20190224022517-abcee96e61b1,h1:1qKTeMTSIEvRIjvVYzgcRp0xVp0eoiRTTiHSncb5gD8=,4decd67f1252df4ee34968cb0cb4e7dc6010302b24ce8edd418f1c2520f1c351
gitlab.com/NebulousLabs/errors,v0.0.0-20171229012116-7ead97ef90b8,h1:gZfMjx7Jr6N8b7iJO4eUjDsn6xJqoyXg8D+ogdoAfKY=,b355474f1a2ef2722ae450ef6df7209d223188ae413706be122b472fcc053c48
gitlab.com/NebulousLabs/fastrand,v0.0.0-20181126182046-603482d69e40,h1:dizWJqTWjwyD8KGcMOwgrkqu1JIkofYgKkmDeNE7oAs=,a56acdda993c7a4795028fe38844d54de9b1877d22e8ae09f205e488ce2284bc
gitlab.com/yumeko/MumbleEmu,v0.0.0-20170923112213-54c9892f02e9,h1:QSaGLacCEAlWXhL/xGZyS3+2aDVvBZe5jcmrDWwXhqs=,51cc295a04dc3b9c39b341f21b95fc42765e3bb61fe30ec2a59fe867c1b5e5ed
go.bug.st/serial.v1,v0.0.0-20180827123349-5f7892a7bb45,h1:mACY1anK6HNCZtm/DK2Rf2ZPHggVqeB0+7rY9Gl6wyI=,f0ea4cd4c51228f1a3cf14c6b92888169944f267e1ee778909512a4c8ac4762f
go.cryptoscope.co/luigi,v0.3.4,h1:eDrtCoUL5Vl2Atr5ty2dq0uFbzFCc6Pz1HEqU1e7I1I=,949612e92dcb2fc919e506740f36d0cfe0797c1f85579a98763aad0135a4580a
go.dedis.ch/fixbuf,v1.0.3,h1:hGcV9Cd/znUxlusJ64eAlExS+5cJDIyTyEG+otu5wQs=,dfa737543a5873b14cdfd0eec675c63044b16d3dbe481b2289c758ae4186ae95
@ -2085,7 +2081,6 @@ layeh.com/radius,v0.0.0-20190322222518-890bc1058917,h1:BDXFaFzUt5EIqe/4wrTc4AcYZ
leb.io/aeshash,v0.0.0-20190627052759-9e6b40329b3b,h1:MG17Tc0pA3XmFTsPwklMMEfcos3pTFnVYM4A0YfVSbU=,a78b48ac18e98ea68dacce16cd94c9074688a0b125f824f047313a33b264ea88
leb.io/hashland,v0.0.0-20171003003232-07375b562dea,h1:s9IkzZTqYqw77voO6taUZHc0C1B096h4T/kQtujGApE=,0698177f24cbde0a7b45495e7fe976fe7623f2b9205995b7d91fd2e7b0f0e243
leb.io/hrff,v0.0.0-20170927164517-757f8bd43e20,h1:9CHS8LIq9MDwUsAaCHUsbUq7zb5lSjLQYWlJ/AbMZKg=,538008712599401a903a7982714c0a9ae745221042d3dfb1437bc508d8fb9e96
llvm.org/llvm,v0.0.0-20191022153947-000000375505,h1:cncItmsQ0kcXFrnkQZv2TGle2ELPCEDi3Q36Kf2T3yg=,3f48da9846fc0f69ccc447ead4480f8c7f2b44b0c24b98a793d36d8cb2a572c0
modernc.org/cc,v1.0.0,h1:nPibNuDEx6tvYrUAtvDTTw98rx5juGsa5zuDnKwEEQQ=,24711e9b28b0d79dd32438eeb7debd86b850350f5f7749b7af640422ecf6b93b
modernc.org/golex,v1.0.0,h1:wWpDlbK8ejRfSyi0frMyhilD3JBvtcx2AdGDnU+JtsE=,335133038991d7feaba5349ac2385db7b49601bba0904abf680803ee2d3c99df
modernc.org/mathutil,v1.0.0,h1:93vKjrJopTPrtTNpZ8XIovER7iCIH1QU7wNbOQXC60I=,766ad95195543fe1ac217ce9f54e1fb43119c25db2b89013b9ef5477ad2dd9d1

1 9fans.net/go v0.0.2 h1:RYM6lWITV8oADrwLfdzxmt8ucfW6UtP9v1jg4qAbqts= 2c42aad9ed60e24046fbf5720f438884942897197cb790ce58cccdacedd9532d
40 gitee.com/nggs/util v0.0.0-20190830024003-3e49d2efc84b h1:6KQpPEs326uPrICQy9x/PxmR8U0v/XsFzpt0k1nFKcY= a062c99c2b560a36168fe51eab8f17f4fadf5d534238881628e83d8d61e51c2a
41 github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1 h1:RMTyvS5bjvSWiUcfqfr/E2pxHEMrALvU+E12n6biymg= 7f068808fc0857d7de8c8f829cc380dce1c6611a3fc819daf4421e9bcb75a07c
42 github.com/99designs/gqlgen v0.10.1 h1:1BgB6XKGTHq7uH4G1/PYyKe2Kz7/vw3AlvMZlD3TEEY= 04b9e7d8a3df6543cd870325b1140ce9ac3f4bbfd8c90ebecec4f908dd420d08
github.com/AlexStocks/log4go v1.0.5 h1:45boeHy0qh0NFBaEhrFT/pUKzQUGf7q2Ux1iQDr/f6o= 59371c2108f62aa9a2233ca8f7de57868ad2c64313b2d68434e0ed6a1748ce2c
43 github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4= 6d7c1af06f8597fde1e86166f26416057392f1b0bdb84f2af555aa461282dd18
44 github.com/AsynkronIT/goconsole v0.0.0-20160504192649-bfa12eebf716 h1:Pk/Kzi5O0T4QxfqvbaUsh8UklbJ9BklZ/ClZBptX5WU= 5a2507b89bb4436881718d785a0ef383652aa99782508b7444cf20255082dab9
45 github.com/Azure/azure-amqp-common-go v1.1.4 h1:DmPXxmLZwi/71CgRTZIKR6yiKEW3eC42S4gSBhfG7y0= 4b800793ff4fefa86a427c445e3a4671b8d1dcd87a44075f6309cace6b0e01e2
242 github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= 614a33736f8b9262a809f101df5bf71f47777879b1191165b6247d6b67c7468c
243 github.com/beevik/guid v0.0.0-20170504223318-d0ea8faecee0 h1:oLd/YLOTOgA4D4aAUhIE8vhl/LAP1ZJrj0mDQpl7GB8= 5add94fcade6c7afa236112c8da300d47ec499ad1789a5e805c8198062dd0749
244 github.com/beevik/ntp v0.2.0 h1:sGsd+kAXzT0bfVfzJfce04g+dSRfrs+tbQW8lweuYgw= 42e14f30c23ba2f5ddaff76101016d87f0f0a0f1d96d3d20e42fd02842091c76
github.com/belogik/goes v0.0.0-20151229125003-e54d722c3aff h1:/kO0p2RTGLB8R5gub7ps0GmYpB2O8LXEoPq8tzFDCUI= f926f1040febe5318efa145541a6fc7898d32514bc13899e812185f05710c5db
245 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 25bd9e2d94aca770e6dbc1f53725f84f6af4432f631d35dd2c46f96ef0512f1a
246 github.com/bep/debounce v1.2.0 h1:wXds8Kq8qRfwAOpAxHrJDbCXgC5aHSzgQb/0gKsHQqo= ddc0a77e4819b6b826d69fdf1a5a153f3f867a31e030cfe28296355b670adf21
247 github.com/bep/gitmap v1.1.1 h1:Nf8ySnC3I7/xPjuWeCwzukUFv185iTUQ6nOvLy9gCJA= 364163e67741ae331d164fd881964160f19fdbdfe094e0e762314cc37aac646a
1459 github.com/opencontainers/runtime-spec v1.0.1 h1:wY4pOY8fBdSIvs9+IDHC55thBuEulhzfSgKeC1yFvzQ= 1958458b00ce912425f5c7d2ee836431b296a3f9320d565512d8c96b107fffbf
1460 github.com/opencontainers/runtime-tools v0.9.0 h1:FYgwVsKRI/H9hU32MJ/4MLOzXWodKK5zsQavY8NPMkU= 53c720dbb7452cfb2fd3945e37c26b5a0140cb1012d35a2b72a5e035f28a32c4
1461 github.com/opencontainers/selinux v1.3.0 h1:xsI95WzPZu5exzA6JzkLSfdr/DilzOhCJOqGe5TgR0g= 88286825b32cd46a0469e578f378a185032da2d5b03893623861ef3af59359d8
github.com/openshift/api v3.9.0+incompatible h1:fJ/KsefYuZAjmrr3+5U9yZIZbTOpVkDDLDLFresAeYs= fc087ac9809ce58bdd15614e04c13f8ecc4a17e71addbe6eb6b777c377b01243
1462 github.com/openshift/client-go v3.9.0+incompatible h1:13k3Ok0B7TA2hA3bQW2aFqn6y04JaJWdk7ITTyg+Ek0= 661b7f28b4905f1936dd58e373374513d54663ec85aecafede1c7d9c260e9369
1463 github.com/openshift/library-go v0.0.0-20191101161407-e7c97b468b83 h1:wwR+laNaFKVGiizoIDL/cAKIZVoKXJ9jbjUoUlq2p5I= c74f8134013f978ef154d6accf9b4b0c5126941f2d45e6eb223db7098f7ab2a4
1464 github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 h1:lM6RxxfUMrYL/f8bWEUqdXrANWtrL7Nndbm9iFN0DlU= 50023eee1ef04412410f43d8b5dcf3ef481c0fc39067add27799654705fa84b2
1879 github.com/zquestz/grab v0.0.0-20190224022517-abcee96e61b1 h1:1qKTeMTSIEvRIjvVYzgcRp0xVp0eoiRTTiHSncb5gD8= 4decd67f1252df4ee34968cb0cb4e7dc6010302b24ce8edd418f1c2520f1c351
1880 gitlab.com/NebulousLabs/errors v0.0.0-20171229012116-7ead97ef90b8 h1:gZfMjx7Jr6N8b7iJO4eUjDsn6xJqoyXg8D+ogdoAfKY= b355474f1a2ef2722ae450ef6df7209d223188ae413706be122b472fcc053c48
1881 gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40 h1:dizWJqTWjwyD8KGcMOwgrkqu1JIkofYgKkmDeNE7oAs= a56acdda993c7a4795028fe38844d54de9b1877d22e8ae09f205e488ce2284bc
gitlab.com/yumeko/MumbleEmu v0.0.0-20170923112213-54c9892f02e9 h1:QSaGLacCEAlWXhL/xGZyS3+2aDVvBZe5jcmrDWwXhqs= 51cc295a04dc3b9c39b341f21b95fc42765e3bb61fe30ec2a59fe867c1b5e5ed
1882 go.bug.st/serial.v1 v0.0.0-20180827123349-5f7892a7bb45 h1:mACY1anK6HNCZtm/DK2Rf2ZPHggVqeB0+7rY9Gl6wyI= f0ea4cd4c51228f1a3cf14c6b92888169944f267e1ee778909512a4c8ac4762f
1883 go.cryptoscope.co/luigi v0.3.4 h1:eDrtCoUL5Vl2Atr5ty2dq0uFbzFCc6Pz1HEqU1e7I1I= 949612e92dcb2fc919e506740f36d0cfe0797c1f85579a98763aad0135a4580a
1884 go.dedis.ch/fixbuf v1.0.3 h1:hGcV9Cd/znUxlusJ64eAlExS+5cJDIyTyEG+otu5wQs= dfa737543a5873b14cdfd0eec675c63044b16d3dbe481b2289c758ae4186ae95
2081 leb.io/aeshash v0.0.0-20190627052759-9e6b40329b3b h1:MG17Tc0pA3XmFTsPwklMMEfcos3pTFnVYM4A0YfVSbU= a78b48ac18e98ea68dacce16cd94c9074688a0b125f824f047313a33b264ea88
2082 leb.io/hashland v0.0.0-20171003003232-07375b562dea h1:s9IkzZTqYqw77voO6taUZHc0C1B096h4T/kQtujGApE= 0698177f24cbde0a7b45495e7fe976fe7623f2b9205995b7d91fd2e7b0f0e243
2083 leb.io/hrff v0.0.0-20170927164517-757f8bd43e20 h1:9CHS8LIq9MDwUsAaCHUsbUq7zb5lSjLQYWlJ/AbMZKg= 538008712599401a903a7982714c0a9ae745221042d3dfb1437bc508d8fb9e96
llvm.org/llvm v0.0.0-20191022153947-000000375505 h1:cncItmsQ0kcXFrnkQZv2TGle2ELPCEDi3Q36Kf2T3yg= 3f48da9846fc0f69ccc447ead4480f8c7f2b44b0c24b98a793d36d8cb2a572c0
2084 modernc.org/cc v1.0.0 h1:nPibNuDEx6tvYrUAtvDTTw98rx5juGsa5zuDnKwEEQQ= 24711e9b28b0d79dd32438eeb7debd86b850350f5f7749b7af640422ecf6b93b
2085 modernc.org/golex v1.0.0 h1:wWpDlbK8ejRfSyi0frMyhilD3JBvtcx2AdGDnU+JtsE= 335133038991d7feaba5349ac2385db7b49601bba0904abf680803ee2d3c99df
2086 modernc.org/mathutil v1.0.0 h1:93vKjrJopTPrtTNpZ8XIovER7iCIH1QU7wNbOQXC60I= 766ad95195543fe1ac217ce9f54e1fb43119c25db2b89013b9ef5477ad2dd9d1

View file

@ -126,7 +126,7 @@ func TestZipSums(t *testing.T) {
test.m.Path = "" // mark for deletion
needUpdate = true
} else {
t.Errorf("%s: could not download mdoule: %s", test.m, err)
t.Errorf("%s: could not download module: %s", test.m, err)
}
return
}

View file

@ -114,7 +114,10 @@ require downgrading other dependencies, and 'go get' does
this automatically as well.
The -insecure flag permits fetching from repositories and resolving
custom domains using insecure schemes such as HTTP. Use with caution.
custom domains using insecure schemes such as HTTP. Use with caution. The
GOINSECURE environment variable is usually a better alternative, since it
provides control over which modules may be retrieved using an insecure scheme.
See 'go help environment' for details.
The second step is to download (if needed), build, and install
the named packages.
@ -307,6 +310,20 @@ func runGet(cmd *base.Command, args []string) {
continue
}
// Guard against 'go get x.go', a common mistake.
// Note that package and module paths may end with '.go', so only print an error
// if the argument has no version and either has no slash or refers to an existing file.
if strings.HasSuffix(arg, ".go") && vers == "" {
if !strings.Contains(arg, "/") {
base.Errorf("go get %s: arguments must be package or module paths", arg)
continue
}
if fi, err := os.Stat(arg); err == nil && !fi.IsDir() {
base.Errorf("go get: %s exists as a file, but 'go get' requires package arguments", arg)
continue
}
}
// If no version suffix is specified, assume @upgrade.
// If -u=patch was specified, assume @patch instead.
if vers == "" {
@ -326,15 +343,22 @@ func runGet(cmd *base.Command, args []string) {
// patterns like golang.org/x/tools/..., which can't be expanded
// during package loading until they're in the build list.
switch {
case search.IsRelativePath(path):
// Relative paths like ../../foo or ../../foo... are restricted to
// matching packages in the main module. If the path is explicit and
// contains no wildcards (...), check that it is a package in
// the main module. If the path contains wildcards but matches no
// packages, we'll warn after package loading.
case filepath.IsAbs(path) || search.IsRelativePath(path):
// Absolute paths like C:\foo and relative paths like ../foo...
// are restricted to matching packages in the main module. If the path
// is explicit and contains no wildcards (...), check that it is a
// package in the main module. If the path contains wildcards but
// matches no packages, we'll warn after package loading.
if !strings.Contains(path, "...") {
pkgPath := modload.DirImportPath(filepath.FromSlash(path))
if pkgs := modload.TargetPackages(pkgPath); len(pkgs) == 0 {
m := search.NewMatch(path)
if pkgPath := modload.DirImportPath(path); pkgPath != "." {
m = modload.TargetPackages(pkgPath)
}
if len(m.Pkgs) == 0 {
for _, err := range m.Errs {
base.Errorf("go get %s: %v", arg, err)
}
abs, err := filepath.Abs(path)
if err != nil {
abs = path
@ -374,7 +398,7 @@ func runGet(cmd *base.Command, args []string) {
default:
// The argument is a package or module path.
if modload.HasModRoot() {
if pkgs := modload.TargetPackages(path); len(pkgs) != 0 {
if m := modload.TargetPackages(path); len(m.Pkgs) != 0 {
// The path is in the main module. Nothing to query.
if vers != "upgrade" && vers != "patch" {
base.Errorf("go get %s: can't request explicit version of path in main module", arg)
@ -520,8 +544,12 @@ func runGet(cmd *base.Command, args []string) {
// If the pattern did not match any packages, look up a new module.
// If the pattern doesn't match anything on the last iteration,
// we'll print a warning after the outer loop.
if !search.IsRelativePath(arg.path) && !match.Literal && arg.path != "all" {
if !match.IsLocal() && !match.IsLiteral() && arg.path != "all" {
addQuery(&query{querySpec: querySpec{path: arg.path, vers: arg.vers}, arg: arg.raw})
} else {
for _, err := range match.Errs {
base.Errorf("go get: %v", err)
}
}
continue
}

View file

@ -205,6 +205,7 @@ func PackageBuildInfo(path string, deps []string) string {
if isStandardImportPath(path) || !Enabled() {
return ""
}
target := mustFindModule(path, path)
mdeps := make(map[module.Version]bool)
for _, dep := range deps {
@ -221,26 +222,25 @@ func PackageBuildInfo(path string, deps []string) string {
var buf bytes.Buffer
fmt.Fprintf(&buf, "path\t%s\n", path)
tv := target.Version
if tv == "" {
tv = "(devel)"
}
fmt.Fprintf(&buf, "mod\t%s\t%s\t%s\n", target.Path, tv, modfetch.Sum(target))
for _, mod := range mods {
mv := mod.Version
writeEntry := func(token string, m module.Version) {
mv := m.Version
if mv == "" {
mv = "(devel)"
}
r := Replacement(mod)
h := ""
if r.Path == "" {
h = "\t" + modfetch.Sum(mod)
}
fmt.Fprintf(&buf, "dep\t%s\t%s%s\n", mod.Path, mv, h)
if r.Path != "" {
fmt.Fprintf(&buf, "=>\t%s\t%s\t%s\n", r.Path, r.Version, modfetch.Sum(r))
fmt.Fprintf(&buf, "%s\t%s\t%s", token, m.Path, mv)
if r := Replacement(m); r.Path == "" {
fmt.Fprintf(&buf, "\t%s\n", modfetch.Sum(m))
} else {
fmt.Fprintf(&buf, "\n=>\t%s\t%s\t%s\n", r.Path, r.Version, modfetch.Sum(r))
}
}
writeEntry("mod", target)
for _, mod := range mods {
writeEntry("dep", mod)
}
return buf.String()
}

View file

@ -363,15 +363,15 @@ variable (see 'go help env'). The default setting for GOPROXY is
Go module mirror run by Google and fall back to a direct connection
if the proxy reports that it does not have the module (HTTP error 404 or 410).
See https://proxy.golang.org/privacy for the service's privacy policy.
If GOPROXY is set to the string "direct", downloads use a direct connection
to source control servers. Setting GOPROXY to "off" disallows downloading
modules from any source. Otherwise, GOPROXY is expected to be a comma-separated
list of the URLs of module proxies, in which case the go command will fetch
modules from those proxies. For each request, the go command tries each proxy
in sequence, only moving to the next if the current proxy returns a 404 or 410
HTTP response. The string "direct" may appear in the proxy list,
to cause a direct connection to be attempted at that point in the search.
Any proxies listed after "direct" are never consulted.
If GOPROXY is set to the string "direct", downloads use a direct connection to
source control servers. Setting GOPROXY to "off" disallows downloading modules
from any source. Otherwise, GOPROXY is expected to be list of module proxy URLs
separated by either comma (,) or pipe (|) characters, which control error
fallback behavior. For each request, the go command tries each proxy in
sequence. If there is an error, the go command will try the next proxy in the
list if the error is a 404 or 410 HTTP response or if the current proxy is
followed by a pipe character, indicating it is safe to fall back on any error.
The GOPRIVATE and GONOPROXY environment variables allow bypassing
the proxy for selected modules. See 'go help module-private' for details.

View file

@ -62,11 +62,15 @@ func (e *ImportMissingError) ImportPath() string {
// modules in the build list, or found in both the main module and its vendor
// directory.
type AmbiguousImportError struct {
ImportPath string
importPath string
Dirs []string
Modules []module.Version // Either empty or 1:1 with Dirs.
}
func (e *AmbiguousImportError) ImportPath() string {
return e.importPath
}
func (e *AmbiguousImportError) Error() string {
locType := "modules"
if len(e.Modules) == 0 {
@ -74,7 +78,7 @@ func (e *AmbiguousImportError) Error() string {
}
var buf strings.Builder
fmt.Fprintf(&buf, "ambiguous import: found package %s in multiple %s:", e.ImportPath, locType)
fmt.Fprintf(&buf, "ambiguous import: found package %s in multiple %s:", e.importPath, locType)
for i, dir := range e.Dirs {
buf.WriteString("\n\t")
@ -93,6 +97,8 @@ func (e *AmbiguousImportError) Error() string {
return buf.String()
}
var _ load.ImportPathError = &AmbiguousImportError{}
// Import finds the module and directory in the build list
// containing the package with the given import path.
// The answer must be unique: Import returns an error
@ -120,7 +126,9 @@ func Import(path string) (m module.Version, dir string, err error) {
pathIsStd := search.IsStandardImportPath(path)
if pathIsStd && goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) {
if targetInGorootSrc {
if dir, ok := dirInModule(path, targetPrefix, ModRoot(), true); ok {
if dir, ok, err := dirInModule(path, targetPrefix, ModRoot(), true); err != nil {
return module.Version{}, dir, err
} else if ok {
return Target, dir, nil
}
}
@ -131,10 +139,10 @@ func Import(path string) (m module.Version, dir string, err error) {
// -mod=vendor is special.
// Everything must be in the main module or the main module's vendor directory.
if cfg.BuildMod == "vendor" {
mainDir, mainOK := dirInModule(path, targetPrefix, ModRoot(), true)
vendorDir, vendorOK := dirInModule(path, "", filepath.Join(ModRoot(), "vendor"), false)
mainDir, mainOK, mainErr := dirInModule(path, targetPrefix, ModRoot(), true)
vendorDir, vendorOK, _ := dirInModule(path, "", filepath.Join(ModRoot(), "vendor"), false)
if mainOK && vendorOK {
return module.Version{}, "", &AmbiguousImportError{ImportPath: path, Dirs: []string{mainDir, vendorDir}}
return module.Version{}, "", &AmbiguousImportError{importPath: path, Dirs: []string{mainDir, vendorDir}}
}
// Prefer to return main directory if there is one,
// Note that we're not checking that the package exists.
@ -142,6 +150,9 @@ func Import(path string) (m module.Version, dir string, err error) {
if !vendorOK && mainDir != "" {
return Target, mainDir, nil
}
if mainErr != nil {
return module.Version{}, "", mainErr
}
readVendorList()
return vendorPkgModule[path], vendorDir, nil
}
@ -164,8 +175,9 @@ func Import(path string) (m module.Version, dir string, err error) {
// not ambiguous.
return module.Version{}, "", err
}
dir, ok := dirInModule(path, m.Path, root, isLocal)
if ok {
if dir, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil {
return module.Version{}, "", err
} else if ok {
mods = append(mods, m)
dirs = append(dirs, dir)
}
@ -174,7 +186,7 @@ func Import(path string) (m module.Version, dir string, err error) {
return mods[0], dirs[0], nil
}
if len(mods) > 0 {
return module.Version{}, "", &AmbiguousImportError{ImportPath: path, Dirs: dirs, Modules: mods}
return module.Version{}, "", &AmbiguousImportError{importPath: path, Dirs: dirs, Modules: mods}
}
// Look up module containing the package, for addition to the build list.
@ -241,8 +253,9 @@ func Import(path string) (m module.Version, dir string, err error) {
// Report fetch error as above.
return module.Version{}, "", err
}
_, ok := dirInModule(path, m.Path, root, isLocal)
if ok {
if _, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil {
return m, "", err
} else if ok {
return m, "", &ImportMissingError{Path: path, Module: m}
}
}
@ -313,19 +326,29 @@ func maybeInModule(path, mpath string) bool {
len(path) > len(mpath) && path[len(mpath)] == '/' && path[:len(mpath)] == mpath
}
var haveGoModCache, haveGoFilesCache par.Cache
var (
haveGoModCache par.Cache // dir → bool
haveGoFilesCache par.Cache // dir → goFilesEntry
)
type goFilesEntry struct {
haveGoFiles bool
err error
}
// dirInModule locates the directory that would hold the package named by the given path,
// if it were in the module with module path mpath and root mdir.
// If path is syntactically not within mpath,
// or if mdir is a local file tree (isLocal == true) and the directory
// that would hold path is in a sub-module (covered by a go.mod below mdir),
// dirInModule returns "", false.
// dirInModule returns "", false, nil.
//
// Otherwise, dirInModule returns the name of the directory where
// Go source files would be expected, along with a boolean indicating
// whether there are in fact Go source files in that directory.
func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFiles bool) {
// A non-nil error indicates that the existence of the directory and/or
// source files could not be determined, for example due to a permission error.
func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFiles bool, err error) {
// Determine where to expect the package.
if path == mpath {
dir = mdir
@ -334,7 +357,7 @@ func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFile
} else if len(path) > len(mpath) && path[len(mpath)] == '/' && path[:len(mpath)] == mpath {
dir = filepath.Join(mdir, path[len(mpath)+1:])
} else {
return "", false
return "", false, nil
}
// Check that there aren't other modules in the way.
@ -351,7 +374,7 @@ func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFile
}).(bool)
if haveGoMod {
return "", false
return "", false, nil
}
parent := filepath.Dir(d)
if parent == d {
@ -368,23 +391,58 @@ func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFile
// Are there Go source files in the directory?
// We don't care about build tags, not even "+build ignore".
// We're just looking for a plausible directory.
haveGoFiles = haveGoFilesCache.Do(dir, func() interface{} {
f, err := os.Open(dir)
if err != nil {
return false
res := haveGoFilesCache.Do(dir, func() interface{} {
ok, err := isDirWithGoFiles(dir)
return goFilesEntry{haveGoFiles: ok, err: err}
}).(goFilesEntry)
return dir, res.haveGoFiles, res.err
}
func isDirWithGoFiles(dir string) (bool, error) {
f, err := os.Open(dir)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
defer f.Close()
names, _ := f.Readdirnames(-1)
for _, name := range names {
if strings.HasSuffix(name, ".go") {
info, err := os.Stat(filepath.Join(dir, name))
if err == nil && info.Mode().IsRegular() {
return true
return false, err
}
defer f.Close()
names, firstErr := f.Readdirnames(-1)
if firstErr != nil {
if fi, err := f.Stat(); err == nil && !fi.IsDir() {
return false, nil
}
// Rewrite the error from ReadDirNames to include the path if not present.
// See https://golang.org/issue/38923.
var pe *os.PathError
if !errors.As(firstErr, &pe) {
firstErr = &os.PathError{Op: "readdir", Path: dir, Err: firstErr}
}
}
for _, name := range names {
if strings.HasSuffix(name, ".go") {
info, err := os.Stat(filepath.Join(dir, name))
if err == nil && info.Mode().IsRegular() {
// If any .go source file exists, the package exists regardless of
// errors for other source files. Leave further error reporting for
// later.
return true, nil
}
if firstErr == nil {
if os.IsNotExist(err) {
// If the file was concurrently deleted, or was a broken symlink,
// convert the error to an opaque error instead of one matching
// os.IsNotExist.
err = errors.New(err.Error())
}
firstErr = err
}
}
return false
}).(bool)
}
return dir, haveGoFiles
return false, firstErr
}

View file

@ -26,7 +26,6 @@ import (
"cmd/go/internal/lockedfile"
"cmd/go/internal/modconv"
"cmd/go/internal/modfetch"
"cmd/go/internal/modfetch/codehost"
"cmd/go/internal/mvs"
"cmd/go/internal/search"
@ -59,27 +58,6 @@ var (
allowMissingModuleImports bool
)
var modFile *modfile.File
// A modFileIndex is an index of data corresponding to a modFile
// at a specific point in time.
type modFileIndex struct {
data []byte
dataNeedsFix bool // true if fixVersion applied a change while parsing data
module module.Version
goVersion string
require map[module.Version]requireMeta
replace map[module.Version]module.Version
exclude map[module.Version]bool
}
// index is the index of the go.mod file as of when it was last read or written.
var index *modFileIndex
type requireMeta struct {
indirect bool
}
// ModFile returns the parsed go.mod file.
//
// Note that after calling ImportPaths or LoadBuildList,
@ -199,17 +177,6 @@ func Init() {
base.Fatalf("$GOPATH/go.mod exists but should not")
}
oldSrcMod := filepath.Join(list[0], "src/mod")
pkgMod := filepath.Join(list[0], "pkg/mod")
infoOld, errOld := os.Stat(oldSrcMod)
_, errMod := os.Stat(pkgMod)
if errOld == nil && infoOld.IsDir() && errMod != nil && os.IsNotExist(errMod) {
os.Rename(oldSrcMod, pkgMod)
}
modfetch.PkgMod = pkgMod
codehost.WorkRoot = filepath.Join(pkgMod, "cache/vcs")
cfg.ModulesEnabled = true
load.ModBinDir = BinDir
load.ModLookup = Lookup
@ -246,13 +213,6 @@ func Init() {
func init() {
load.ModInit = Init
// Set modfetch.PkgMod and codehost.WorkRoot unconditionally,
// so that go clean -modcache and go mod download can run even without modules enabled.
if list := filepath.SplitList(cfg.BuildContext.GOPATH); len(list) > 0 && list[0] != "" {
modfetch.PkgMod = filepath.Join(list[0], "pkg/mod")
codehost.WorkRoot = filepath.Join(list[0], "pkg/mod/cache/vcs")
}
}
// WillBeEnabled checks whether modules should be enabled but does not
@ -555,101 +515,6 @@ func setDefaultBuildMod() {
}
}
// checkVendorConsistency verifies that the vendor/modules.txt file matches (if
// go 1.14) or at least does not contradict (go 1.13 or earlier) the
// requirements and replacements listed in the main module's go.mod file.
func checkVendorConsistency() {
readVendorList()
pre114 := false
if modFile.Go == nil || semver.Compare("v"+modFile.Go.Version, "v1.14") < 0 {
// Go versions before 1.14 did not include enough information in
// vendor/modules.txt to check for consistency.
// If we know that we're on an earlier version, relax the consistency check.
pre114 = true
}
vendErrors := new(strings.Builder)
vendErrorf := func(mod module.Version, format string, args ...interface{}) {
detail := fmt.Sprintf(format, args...)
if mod.Version == "" {
fmt.Fprintf(vendErrors, "\n\t%s: %s", mod.Path, detail)
} else {
fmt.Fprintf(vendErrors, "\n\t%s@%s: %s", mod.Path, mod.Version, detail)
}
}
for _, r := range modFile.Require {
if !vendorMeta[r.Mod].Explicit {
if pre114 {
// Before 1.14, modules.txt did not indicate whether modules were listed
// explicitly in the main module's go.mod file.
// However, we can at least detect a version mismatch if packages were
// vendored from a non-matching version.
if vv, ok := vendorVersion[r.Mod.Path]; ok && vv != r.Mod.Version {
vendErrorf(r.Mod, fmt.Sprintf("is explicitly required in go.mod, but vendor/modules.txt indicates %s@%s", r.Mod.Path, vv))
}
} else {
vendErrorf(r.Mod, "is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt")
}
}
}
describe := func(m module.Version) string {
if m.Version == "" {
return m.Path
}
return m.Path + "@" + m.Version
}
// We need to verify *all* replacements that occur in modfile: even if they
// don't directly apply to any module in the vendor list, the replacement
// go.mod file can affect the selected versions of other (transitive)
// dependencies
for _, r := range modFile.Replace {
vr := vendorMeta[r.Old].Replacement
if vr == (module.Version{}) {
if pre114 && (r.Old.Version == "" || vendorVersion[r.Old.Path] != r.Old.Version) {
// Before 1.14, modules.txt omitted wildcard replacements and
// replacements for modules that did not have any packages to vendor.
} else {
vendErrorf(r.Old, "is replaced in go.mod, but not marked as replaced in vendor/modules.txt")
}
} else if vr != r.New {
vendErrorf(r.Old, "is replaced by %s in go.mod, but marked as replaced by %s in vendor/modules.txt", describe(r.New), describe(vr))
}
}
for _, mod := range vendorList {
meta := vendorMeta[mod]
if meta.Explicit {
if _, inGoMod := index.require[mod]; !inGoMod {
vendErrorf(mod, "is marked as explicit in vendor/modules.txt, but not explicitly required in go.mod")
}
}
}
for _, mod := range vendorReplaced {
r := Replacement(mod)
if r == (module.Version{}) {
vendErrorf(mod, "is marked as replaced in vendor/modules.txt, but not replaced in go.mod")
continue
}
if meta := vendorMeta[mod]; r != meta.Replacement {
vendErrorf(mod, "is marked as replaced by %s in vendor/modules.txt, but replaced by %s in go.mod", describe(meta.Replacement), describe(r))
}
}
if vendErrors.Len() > 0 {
base.Fatalf("go: inconsistent vendoring in %s:%s\n\nrun 'go mod vendor' to sync, or use -mod=mod or -mod=readonly to ignore the vendor directory", modRoot, vendErrors)
}
}
// Allowed reports whether module m is allowed (not excluded) by the main module's go.mod.
func Allowed(m module.Version) bool {
return index == nil || !index.exclude[m]
}
func legacyModInit() {
if modFile == nil {
path, err := findModulePath(modRoot)
@ -740,13 +605,14 @@ func findAltConfig(dir string) (root, name string) {
panic("dir not set")
}
dir = filepath.Clean(dir)
if rel := search.InDir(dir, cfg.BuildContext.GOROOT); rel != "" {
// Don't suggest creating a module from $GOROOT/.git/config
// or a config file found in any parent of $GOROOT (see #34191).
return "", ""
}
for {
for _, name := range altConfigs {
if fi, err := os.Stat(filepath.Join(dir, name)); err == nil && !fi.IsDir() {
if rel := search.InDir(dir, cfg.BuildContext.GOROOT); rel == "." {
// Don't suggest creating a module from $GOROOT/.git/config.
return "", ""
}
return dir, name
}
}
@ -983,113 +849,3 @@ func WriteGoMod() {
base.Fatalf("go: updating go.mod: %v", err)
}
}
// indexModFile rebuilds the index of modFile.
// If modFile has been changed since it was first read,
// modFile.Cleanup must be called before indexModFile.
func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileIndex {
i := new(modFileIndex)
i.data = data
i.dataNeedsFix = needsFix
i.module = module.Version{}
if modFile.Module != nil {
i.module = modFile.Module.Mod
}
i.goVersion = ""
if modFile.Go != nil {
i.goVersion = modFile.Go.Version
}
i.require = make(map[module.Version]requireMeta, len(modFile.Require))
for _, r := range modFile.Require {
i.require[r.Mod] = requireMeta{indirect: r.Indirect}
}
i.replace = make(map[module.Version]module.Version, len(modFile.Replace))
for _, r := range modFile.Replace {
if prev, dup := i.replace[r.Old]; dup && prev != r.New {
base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v", r.Old, prev, r.New)
}
i.replace[r.Old] = r.New
}
i.exclude = make(map[module.Version]bool, len(modFile.Exclude))
for _, x := range modFile.Exclude {
i.exclude[x.Mod] = true
}
return i
}
// modFileIsDirty reports whether the go.mod file differs meaningfully
// from what was indexed.
// If modFile has been changed (even cosmetically) since it was first read,
// modFile.Cleanup must be called before modFileIsDirty.
func (i *modFileIndex) modFileIsDirty(modFile *modfile.File) bool {
if i == nil {
return modFile != nil
}
if i.dataNeedsFix {
return true
}
if modFile.Module == nil {
if i.module != (module.Version{}) {
return true
}
} else if modFile.Module.Mod != i.module {
return true
}
if modFile.Go == nil {
if i.goVersion != "" {
return true
}
} else if modFile.Go.Version != i.goVersion {
if i.goVersion == "" && cfg.BuildMod == "readonly" {
// go.mod files did not always require a 'go' version, so do not error out
// if one is missing — we may be inside an older module in the module
// cache, and should bias toward providing useful behavior.
} else {
return true
}
}
if len(modFile.Require) != len(i.require) ||
len(modFile.Replace) != len(i.replace) ||
len(modFile.Exclude) != len(i.exclude) {
return true
}
for _, r := range modFile.Require {
if meta, ok := i.require[r.Mod]; !ok {
return true
} else if r.Indirect != meta.indirect {
if cfg.BuildMod == "readonly" {
// The module's requirements are consistent; only the "// indirect"
// comments that are wrong. But those are only guaranteed to be accurate
// after a "go mod tidy" — it's a good idea to run those before
// committing a change, but it's certainly not mandatory.
} else {
return true
}
}
}
for _, r := range modFile.Replace {
if r.New != i.replace[r.Old] {
return true
}
}
for _, x := range modFile.Exclude {
if !i.exclude[x.Mod] {
return true
}
}
return false
}

View file

@ -6,18 +6,6 @@ package modload
import (
"bytes"
"errors"
"fmt"
"go/build"
"io/ioutil"
"os"
"path"
pathpkg "path"
"path/filepath"
"sort"
"strings"
"sync"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/imports"
@ -26,10 +14,17 @@ import (
"cmd/go/internal/par"
"cmd/go/internal/search"
"cmd/go/internal/str"
"errors"
"fmt"
"go/build"
"os"
"path"
pathpkg "path"
"path/filepath"
"sort"
"strings"
"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
)
// buildList is the list of modules to use for building packages.
@ -65,23 +60,13 @@ func ImportPaths(patterns []string) []*search.Match {
// packages. The build tags should typically be imports.Tags() or
// imports.AnyTags(); a nil map has no special meaning.
func ImportPathsQuiet(patterns []string, tags map[string]bool) []*search.Match {
var fsDirs [][]string
updateMatches := func(matches []*search.Match, iterating bool) {
for i, m := range matches {
for _, m := range matches {
switch {
case build.IsLocalImport(m.Pattern) || filepath.IsAbs(m.Pattern):
case m.IsLocal():
// Evaluate list of file system directories on first iteration.
if fsDirs == nil {
fsDirs = make([][]string, len(matches))
}
if fsDirs[i] == nil {
var dirs []string
if m.Literal {
dirs = []string{m.Pattern}
} else {
dirs = search.MatchPackagesInFS(m.Pattern).Pkgs
}
fsDirs[i] = dirs
if m.Dirs == nil {
matchLocalDirs(m)
}
// Make a copy of the directory list and translate to import paths.
@ -90,102 +75,53 @@ func ImportPathsQuiet(patterns []string, tags map[string]bool) []*search.Match {
// from not being in the build list to being in it and back as
// the exact version of a particular module increases during
// the loader iterations.
m.Pkgs = str.StringList(fsDirs[i])
pkgs := m.Pkgs
m.Pkgs = m.Pkgs[:0]
for _, pkg := range pkgs {
var dir string
if !filepath.IsAbs(pkg) {
dir = filepath.Join(base.Cwd, pkg)
} else {
dir = filepath.Clean(pkg)
}
for _, dir := range m.Dirs {
pkg, err := resolveLocalPackage(dir)
if err != nil {
if !m.IsLiteral() && (err == errPkgIsBuiltin || err == errPkgIsGorootSrc) {
continue // Don't include "builtin" or GOROOT/src in wildcard patterns.
}
// golang.org/issue/32917: We should resolve a relative path to a
// package path only if the relative path actually contains the code
// for that package.
if !dirContainsPackage(dir) {
// If we're outside of a module, ensure that the failure mode
// indicates that.
ModRoot()
// If the directory is local but does not exist, don't return it
// while loader is iterating, since this might trigger a fetch.
// After loader is done iterating, we still need to return the
// path, so that "go list -e" produces valid output.
if !iterating {
// We don't have a valid path to resolve to, so report the
// unresolved path.
m.Pkgs = append(m.Pkgs, pkg)
m.AddError(err)
}
continue
}
// Note: The checks for @ here are just to avoid misinterpreting
// the module cache directories (formerly GOPATH/src/mod/foo@v1.5.2/bar).
// It's not strictly necessary but helpful to keep the checks.
if modRoot != "" && dir == modRoot {
pkg = targetPrefix
} else if modRoot != "" && strings.HasPrefix(dir, modRoot+string(filepath.Separator)) && !strings.Contains(dir[len(modRoot):], "@") {
suffix := filepath.ToSlash(dir[len(modRoot):])
if strings.HasPrefix(suffix, "/vendor/") {
// TODO getmode vendor check
pkg = strings.TrimPrefix(suffix, "/vendor/")
} else if targetInGorootSrc && Target.Path == "std" {
// Don't add the prefix "std/" to packages in the "std" module.
// It's the one module path that isn't a prefix of its packages.
pkg = strings.TrimPrefix(suffix, "/")
if pkg == "builtin" {
// "builtin" is a pseudo-package with a real source file.
// It's not included in "std", so it shouldn't be included in
// "./..." within module "std" either.
continue
}
} else {
modPkg := targetPrefix + suffix
if _, ok := dirInModule(modPkg, targetPrefix, modRoot, true); ok {
pkg = modPkg
} else if !iterating {
ModRoot()
base.Errorf("go: directory %s is outside main module", base.ShortPath(dir))
}
}
} else if sub := search.InDir(dir, cfg.GOROOTsrc); sub != "" && sub != "." && !strings.Contains(sub, "@") {
pkg = filepath.ToSlash(sub)
} else if path := pathInModuleCache(dir); path != "" {
pkg = path
} else {
pkg = ""
if !iterating {
ModRoot()
base.Errorf("go: directory %s outside available modules", base.ShortPath(dir))
}
}
m.Pkgs = append(m.Pkgs, pkg)
}
case strings.Contains(m.Pattern, "..."):
m.Pkgs = matchPackages(m.Pattern, loaded.tags, true, buildList)
case m.IsLiteral():
m.Pkgs = []string{m.Pattern()}
case m.Pattern == "all":
case strings.Contains(m.Pattern(), "..."):
m.Errs = m.Errs[:0]
matchPackages(m, loaded.tags, includeStd, buildList)
case m.Pattern() == "all":
loaded.testAll = true
if iterating {
// Enumerate the packages in the main module.
// We'll load the dependencies as we find them.
m.Pkgs = matchPackages("...", loaded.tags, false, []module.Version{Target})
m.Errs = m.Errs[:0]
matchPackages(m, loaded.tags, omitStd, []module.Version{Target})
} else {
// Starting with the packages in the main module,
// enumerate the full list of "all".
m.Pkgs = loaded.computePatternAll(m.Pkgs)
}
case search.IsMetaPackage(m.Pattern): // std, cmd
if len(m.Pkgs) == 0 {
m.Pkgs = search.MatchPackages(m.Pattern).Pkgs
case m.Pattern() == "std" || m.Pattern() == "cmd":
if m.Pkgs == nil {
m.MatchPackages() // Locate the packages within GOROOT/src.
}
default:
m.Pkgs = []string{m.Pattern}
panic(fmt.Sprintf("internal error: modload missing case for pattern %s", m.Pattern()))
}
}
}
@ -194,10 +130,7 @@ func ImportPathsQuiet(patterns []string, tags map[string]bool) []*search.Match {
var matches []*search.Match
for _, pattern := range search.CleanPatterns(patterns) {
matches = append(matches, &search.Match{
Pattern: pattern,
Literal: !strings.Contains(pattern, "...") && !search.IsMetaPackage(pattern),
})
matches = append(matches, search.NewMatch(pattern))
}
loaded = newLoader(tags)
@ -238,6 +171,139 @@ func checkMultiplePaths() {
base.ExitIfErrors()
}
// matchLocalDirs is like m.MatchDirs, but tries to avoid scanning directories
// outside of the standard library and active modules.
func matchLocalDirs(m *search.Match) {
if !m.IsLocal() {
panic(fmt.Sprintf("internal error: resolveLocalDirs on non-local pattern %s", m.Pattern()))
}
if i := strings.Index(m.Pattern(), "..."); i >= 0 {
// The pattern is local, but it is a wildcard. Its packages will
// only resolve to paths if they are inside of the standard
// library, the main module, or some dependency of the main
// module. Verify that before we walk the filesystem: a filesystem
// walk in a directory like /var or /etc can be very expensive!
dir := filepath.Dir(filepath.Clean(m.Pattern()[:i+3]))
absDir := dir
if !filepath.IsAbs(dir) {
absDir = filepath.Join(base.Cwd, dir)
}
if search.InDir(absDir, cfg.GOROOTsrc) == "" && search.InDir(absDir, ModRoot()) == "" && pathInModuleCache(absDir) == "" {
m.Dirs = []string{}
m.AddError(fmt.Errorf("directory prefix %s outside available modules", base.ShortPath(absDir)))
return
}
}
m.MatchDirs()
}
// resolveLocalPackage resolves a filesystem path to a package path.
func resolveLocalPackage(dir string) (string, error) {
var absDir string
if filepath.IsAbs(dir) {
absDir = filepath.Clean(dir)
} else {
absDir = filepath.Join(base.Cwd, dir)
}
bp, err := cfg.BuildContext.ImportDir(absDir, 0)
if err != nil && (bp == nil || len(bp.IgnoredGoFiles) == 0) {
// golang.org/issue/32917: We should resolve a relative path to a
// package path only if the relative path actually contains the code
// for that package.
//
// If the named directory does not exist or contains no Go files,
// the package does not exist.
// Other errors may affect package loading, but not resolution.
if _, err := os.Stat(absDir); err != nil {
if os.IsNotExist(err) {
// Canonicalize OS-specific errors to errDirectoryNotFound so that error
// messages will be easier for users to search for.
return "", &os.PathError{Op: "stat", Path: absDir, Err: errDirectoryNotFound}
}
return "", err
}
if _, noGo := err.(*build.NoGoError); noGo {
// A directory that does not contain any Go source files — even ignored
// ones! — is not a Go package, and we can't resolve it to a package
// path because that path could plausibly be provided by some other
// module.
//
// Any other error indicates that the package “exists” (at least in the
// sense that it cannot exist in any other module), but has some other
// problem (such as a syntax error).
return "", err
}
}
if modRoot != "" && absDir == modRoot {
if absDir == cfg.GOROOTsrc {
return "", errPkgIsGorootSrc
}
return targetPrefix, nil
}
// Note: The checks for @ here are just to avoid misinterpreting
// the module cache directories (formerly GOPATH/src/mod/foo@v1.5.2/bar).
// It's not strictly necessary but helpful to keep the checks.
if modRoot != "" && strings.HasPrefix(absDir, modRoot+string(filepath.Separator)) && !strings.Contains(absDir[len(modRoot):], "@") {
suffix := filepath.ToSlash(absDir[len(modRoot):])
if strings.HasPrefix(suffix, "/vendor/") {
if cfg.BuildMod != "vendor" {
return "", fmt.Errorf("without -mod=vendor, directory %s has no package path", absDir)
}
readVendorList()
pkg := strings.TrimPrefix(suffix, "/vendor/")
if _, ok := vendorPkgModule[pkg]; !ok {
return "", fmt.Errorf("directory %s is not a package listed in vendor/modules.txt", absDir)
}
return pkg, nil
}
if targetPrefix == "" {
pkg := strings.TrimPrefix(suffix, "/")
if pkg == "builtin" {
// "builtin" is a pseudo-package with a real source file.
// It's not included in "std", so it shouldn't resolve from "."
// within module "std" either.
return "", errPkgIsBuiltin
}
return pkg, nil
}
pkg := targetPrefix + suffix
if _, ok, err := dirInModule(pkg, targetPrefix, modRoot, true); err != nil {
return "", err
} else if !ok {
return "", &PackageNotInModuleError{Mod: Target, Pattern: pkg}
}
return pkg, nil
}
if sub := search.InDir(absDir, cfg.GOROOTsrc); sub != "" && sub != "." && !strings.Contains(sub, "@") {
pkg := filepath.ToSlash(sub)
if pkg == "builtin" {
return "", errPkgIsBuiltin
}
return pkg, nil
}
pkg := pathInModuleCache(absDir)
if pkg == "" {
return "", fmt.Errorf("directory %s outside available modules", base.ShortPath(absDir))
}
return pkg, nil
}
var (
errDirectoryNotFound = errors.New("directory not found")
errPkgIsGorootSrc = errors.New("GOROOT/src is not an importable package")
errPkgIsBuiltin = errors.New(`"builtin" is a pseudo-package, not an importable package`)
)
// pathInModuleCache returns the import path of the directory dir,
// if dir is in the module cache copy of a module in our build list.
func pathInModuleCache(dir string) string {
@ -267,32 +333,6 @@ func pathInModuleCache(dir string) string {
return ""
}
var dirContainsPackageCache sync.Map // absolute dir → bool
func dirContainsPackage(dir string) bool {
isPkg, ok := dirContainsPackageCache.Load(dir)
if !ok {
_, err := cfg.BuildContext.ImportDir(dir, 0)
if err == nil {
isPkg = true
} else {
if fi, statErr := os.Stat(dir); statErr != nil || !fi.IsDir() {
// A non-directory or inaccessible directory is not a Go package.
isPkg = false
} else if _, noGo := err.(*build.NoGoError); noGo {
// A directory containing no Go source files is not a Go package.
isPkg = false
} else {
// An error other than *build.NoGoError indicates that the package exists
// but has some other problem (such as a syntax error).
isPkg = true
}
}
isPkg, _ = dirContainsPackageCache.LoadOrStore(dir, isPkg)
}
return isPkg.(bool)
}
// ImportFromFiles adds modules to the build list as needed
// to satisfy the imports in the named Go source files.
func ImportFromFiles(gofiles []string) {
@ -386,7 +426,7 @@ func loadAll(testAll bool) []string {
loaded.testRoots = true
}
all := TargetPackages("...")
loaded.load(func() []string { return all })
loaded.load(func() []string { return all.Pkgs })
checkMultiplePaths()
WriteGoMod()
@ -398,6 +438,9 @@ func loadAll(testAll bool) []string {
}
paths = append(paths, pkg.path)
}
for _, err := range all.Errs {
base.Errorf("%v", err)
}
base.ExitIfErrors()
return paths
}
@ -405,12 +448,14 @@ func loadAll(testAll bool) []string {
// TargetPackages returns the list of packages in the target (top-level) module
// matching pattern, which may be relative to the working directory, under all
// build tag settings.
func TargetPackages(pattern string) []string {
func TargetPackages(pattern string) *search.Match {
// TargetPackages is relative to the main module, so ensure that the main
// module is a thing that can contain packages.
ModRoot()
return matchPackages(pattern, imports.AnyTags(), false, []module.Version{Target})
m := search.NewMatch(pattern)
matchPackages(m, imports.AnyTags(), omitStd, []module.Version{Target})
return m
}
// BuildList returns the module build list,
@ -655,13 +700,13 @@ func (ld *loader) load(roots func() []string) {
if err.newMissingVersion != "" {
base.Fatalf("go: %s: package provided by %s at latest version %s but not at required version %s", pkg.stackText(), err.Module.Path, err.Module.Version, err.newMissingVersion)
}
fmt.Fprintf(os.Stderr, "go: found %s in %s %s\n", pkg.path, err.Module.Path, err.Module.Version)
if added[pkg.path] {
base.Fatalf("go: %s: looping trying to add package", pkg.stackText())
}
added[pkg.path] = true
numAdded++
if !haveMod[err.Module] {
fmt.Fprintf(os.Stderr, "go: found %s in %s %s\n", pkg.path, err.Module.Path, err.Module.Version)
haveMod[err.Module] = true
modAddedBy[err.Module] = pkg
buildList = append(buildList, err.Module)
@ -761,7 +806,7 @@ func (ld *loader) doPkg(item interface{}) {
// Leave for error during load.
return
}
if build.IsLocalImport(pkg.path) {
if build.IsLocalImport(pkg.path) || filepath.IsAbs(pkg.path) {
// Leave for error during load.
// (Module mode does not allow local imports.)
return
@ -996,354 +1041,3 @@ func WhyDepth(path string) int {
}
return n
}
// Replacement returns the replacement for mod, if any, from go.mod.
// If there is no replacement for mod, Replacement returns
// a module.Version with Path == "".
func Replacement(mod module.Version) module.Version {
if index != nil {
if r, ok := index.replace[mod]; ok {
return r
}
if r, ok := index.replace[module.Version{Path: mod.Path}]; ok {
return r
}
}
return module.Version{}
}
// mvsReqs implements mvs.Reqs for module semantic versions,
// with any exclusions or replacements applied internally.
type mvsReqs struct {
buildList []module.Version
cache par.Cache
versions sync.Map
}
// Reqs returns the current module requirement graph.
// Future calls to SetBuildList do not affect the operation
// of the returned Reqs.
func Reqs() mvs.Reqs {
r := &mvsReqs{
buildList: buildList,
}
return r
}
func (r *mvsReqs) Required(mod module.Version) ([]module.Version, error) {
type cached struct {
list []module.Version
err error
}
c := r.cache.Do(mod, func() interface{} {
list, err := r.required(mod)
if err != nil {
return cached{nil, err}
}
for i, mv := range list {
if index != nil {
for index.exclude[mv] {
mv1, err := r.next(mv)
if err != nil {
return cached{nil, err}
}
if mv1.Version == "none" {
return cached{nil, fmt.Errorf("%s(%s) depends on excluded %s(%s) with no newer version available", mod.Path, mod.Version, mv.Path, mv.Version)}
}
mv = mv1
}
}
list[i] = mv
}
return cached{list, nil}
}).(cached)
return c.list, c.err
}
var vendorOnce sync.Once
type vendorMetadata struct {
Explicit bool
Replacement module.Version
}
var (
vendorList []module.Version // modules that contribute packages to the build, in order of appearance
vendorReplaced []module.Version // all replaced modules; may or may not also contribute packages
vendorVersion map[string]string // module path → selected version (if known)
vendorPkgModule map[string]module.Version // package → containing module
vendorMeta map[module.Version]vendorMetadata
)
// readVendorList reads the list of vendored modules from vendor/modules.txt.
func readVendorList() {
vendorOnce.Do(func() {
vendorList = nil
vendorPkgModule = make(map[string]module.Version)
vendorVersion = make(map[string]string)
vendorMeta = make(map[module.Version]vendorMetadata)
data, err := ioutil.ReadFile(filepath.Join(ModRoot(), "vendor/modules.txt"))
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
base.Fatalf("go: %s", err)
}
return
}
var mod module.Version
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, "# ") {
f := strings.Fields(line)
if len(f) < 3 {
continue
}
if semver.IsValid(f[2]) {
// A module, but we don't yet know whether it is in the build list or
// only included to indicate a replacement.
mod = module.Version{Path: f[1], Version: f[2]}
f = f[3:]
} else if f[2] == "=>" {
// A wildcard replacement found in the main module's go.mod file.
mod = module.Version{Path: f[1]}
f = f[2:]
} else {
// Not a version or a wildcard replacement.
// We don't know how to interpret this module line, so ignore it.
mod = module.Version{}
continue
}
if len(f) >= 2 && f[0] == "=>" {
meta := vendorMeta[mod]
if len(f) == 2 {
// File replacement.
meta.Replacement = module.Version{Path: f[1]}
vendorReplaced = append(vendorReplaced, mod)
} else if len(f) == 3 && semver.IsValid(f[2]) {
// Path and version replacement.
meta.Replacement = module.Version{Path: f[1], Version: f[2]}
vendorReplaced = append(vendorReplaced, mod)
} else {
// We don't understand this replacement. Ignore it.
}
vendorMeta[mod] = meta
}
continue
}
// Not a module line. Must be a package within a module or a metadata
// directive, either of which requires a preceding module line.
if mod.Path == "" {
continue
}
if strings.HasPrefix(line, "## ") {
// Metadata. Take the union of annotations across multiple lines, if present.
meta := vendorMeta[mod]
for _, entry := range strings.Split(strings.TrimPrefix(line, "## "), ";") {
entry = strings.TrimSpace(entry)
if entry == "explicit" {
meta.Explicit = true
}
// All other tokens are reserved for future use.
}
vendorMeta[mod] = meta
continue
}
if f := strings.Fields(line); len(f) == 1 && module.CheckImportPath(f[0]) == nil {
// A package within the current module.
vendorPkgModule[f[0]] = mod
// Since this module provides a package for the build, we know that it
// is in the build list and is the selected version of its path.
// If this information is new, record it.
if v, ok := vendorVersion[mod.Path]; !ok || semver.Compare(v, mod.Version) < 0 {
vendorList = append(vendorList, mod)
vendorVersion[mod.Path] = mod.Version
}
}
}
})
}
func (r *mvsReqs) modFileToList(f *modfile.File) []module.Version {
list := make([]module.Version, 0, len(f.Require))
for _, r := range f.Require {
list = append(list, r.Mod)
}
return list
}
// required returns a unique copy of the requirements of mod.
func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
if mod == Target {
if modFile != nil && modFile.Go != nil {
r.versions.LoadOrStore(mod, modFile.Go.Version)
}
return append([]module.Version(nil), r.buildList[1:]...), nil
}
if cfg.BuildMod == "vendor" {
// For every module other than the target,
// return the full list of modules from modules.txt.
readVendorList()
return append([]module.Version(nil), vendorList...), nil
}
origPath := mod.Path
if repl := Replacement(mod); repl.Path != "" {
if repl.Version == "" {
// TODO: need to slip the new version into the tags list etc.
dir := repl.Path
if !filepath.IsAbs(dir) {
dir = filepath.Join(ModRoot(), dir)
}
gomod := filepath.Join(dir, "go.mod")
data, err := ioutil.ReadFile(gomod)
if err != nil {
return nil, fmt.Errorf("parsing %s: %v", base.ShortPath(gomod), err)
}
f, err := modfile.ParseLax(gomod, data, nil)
if err != nil {
return nil, fmt.Errorf("parsing %s: %v", base.ShortPath(gomod), err)
}
if f.Go != nil {
r.versions.LoadOrStore(mod, f.Go.Version)
}
return r.modFileToList(f), nil
}
mod = repl
}
if mod.Version == "none" {
return nil, nil
}
if !semver.IsValid(mod.Version) {
// Disallow the broader queries supported by fetch.Lookup.
base.Fatalf("go: internal error: %s@%s: unexpected invalid semantic version", mod.Path, mod.Version)
}
data, err := modfetch.GoMod(mod.Path, mod.Version)
if err != nil {
return nil, err
}
f, err := modfile.ParseLax("go.mod", data, nil)
if err != nil {
return nil, module.VersionError(mod, fmt.Errorf("parsing go.mod: %v", err))
}
if f.Module == nil {
return nil, module.VersionError(mod, errors.New("parsing go.mod: missing module line"))
}
if mpath := f.Module.Mod.Path; mpath != origPath && mpath != mod.Path {
return nil, module.VersionError(mod, fmt.Errorf(`parsing go.mod:
module declares its path as: %s
but was required as: %s`, mpath, mod.Path))
}
if f.Go != nil {
r.versions.LoadOrStore(mod, f.Go.Version)
}
return r.modFileToList(f), nil
}
func (*mvsReqs) Max(v1, v2 string) string {
if v1 != "" && semver.Compare(v1, v2) == -1 {
return v2
}
return v1
}
// Upgrade is a no-op, here to implement mvs.Reqs.
// The upgrade logic for go get -u is in ../modget/get.go.
func (*mvsReqs) Upgrade(m module.Version) (module.Version, error) {
return m, nil
}
func versions(path string) ([]string, error) {
// Note: modfetch.Lookup and repo.Versions are cached,
// so there's no need for us to add extra caching here.
var versions []string
err := modfetch.TryProxies(func(proxy string) error {
repo, err := modfetch.Lookup(proxy, path)
if err == nil {
versions, err = repo.Versions("")
}
return err
})
return versions, err
}
// Previous returns the tagged version of m.Path immediately prior to
// m.Version, or version "none" if no prior version is tagged.
func (*mvsReqs) Previous(m module.Version) (module.Version, error) {
list, err := versions(m.Path)
if err != nil {
return module.Version{}, err
}
i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) >= 0 })
if i > 0 {
return module.Version{Path: m.Path, Version: list[i-1]}, nil
}
return module.Version{Path: m.Path, Version: "none"}, nil
}
// next returns the next version of m.Path after m.Version.
// It is only used by the exclusion processing in the Required method,
// not called directly by MVS.
func (*mvsReqs) next(m module.Version) (module.Version, error) {
list, err := versions(m.Path)
if err != nil {
return module.Version{}, err
}
i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) > 0 })
if i < len(list) {
return module.Version{Path: m.Path, Version: list[i]}, nil
}
return module.Version{Path: m.Path, Version: "none"}, nil
}
// fetch downloads the given module (or its replacement)
// and returns its location.
//
// The isLocal return value reports whether the replacement,
// if any, is local to the filesystem.
func fetch(mod module.Version) (dir string, isLocal bool, err error) {
if mod == Target {
return ModRoot(), true, nil
}
if r := Replacement(mod); r.Path != "" {
if r.Version == "" {
dir = r.Path
if !filepath.IsAbs(dir) {
dir = filepath.Join(ModRoot(), dir)
}
// Ensure that the replacement directory actually exists:
// dirInModule does not report errors for missing modules,
// so if we don't report the error now, later failures will be
// very mysterious.
if _, err := os.Stat(dir); err != nil {
if os.IsNotExist(err) {
// Semantically the module version itself “exists” — we just don't
// have its source code. Remove the equivalence to os.ErrNotExist,
// and make the message more concise while we're at it.
err = fmt.Errorf("replacement directory %s does not exist", r.Path)
} else {
err = fmt.Errorf("replacement directory %s: %w", r.Path, err)
}
return dir, true, module.VersionError(mod, err)
}
return dir, true, nil
}
mod = r
}
dir, err = modfetch.Download(mod)
return dir, false, err
}

View file

@ -0,0 +1,164 @@
// Copyright 2020 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 modload
import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
)
var modFile *modfile.File
// A modFileIndex is an index of data corresponding to a modFile
// at a specific point in time.
type modFileIndex struct {
data []byte
dataNeedsFix bool // true if fixVersion applied a change while parsing data
module module.Version
goVersion string
require map[module.Version]requireMeta
replace map[module.Version]module.Version
exclude map[module.Version]bool
}
// index is the index of the go.mod file as of when it was last read or written.
var index *modFileIndex
type requireMeta struct {
indirect bool
}
// Allowed reports whether module m is allowed (not excluded) by the main module's go.mod.
func Allowed(m module.Version) bool {
return index == nil || !index.exclude[m]
}
// Replacement returns the replacement for mod, if any, from go.mod.
// If there is no replacement for mod, Replacement returns
// a module.Version with Path == "".
func Replacement(mod module.Version) module.Version {
if index != nil {
if r, ok := index.replace[mod]; ok {
return r
}
if r, ok := index.replace[module.Version{Path: mod.Path}]; ok {
return r
}
}
return module.Version{}
}
// indexModFile rebuilds the index of modFile.
// If modFile has been changed since it was first read,
// modFile.Cleanup must be called before indexModFile.
func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileIndex {
i := new(modFileIndex)
i.data = data
i.dataNeedsFix = needsFix
i.module = module.Version{}
if modFile.Module != nil {
i.module = modFile.Module.Mod
}
i.goVersion = ""
if modFile.Go != nil {
i.goVersion = modFile.Go.Version
}
i.require = make(map[module.Version]requireMeta, len(modFile.Require))
for _, r := range modFile.Require {
i.require[r.Mod] = requireMeta{indirect: r.Indirect}
}
i.replace = make(map[module.Version]module.Version, len(modFile.Replace))
for _, r := range modFile.Replace {
if prev, dup := i.replace[r.Old]; dup && prev != r.New {
base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v", r.Old, prev, r.New)
}
i.replace[r.Old] = r.New
}
i.exclude = make(map[module.Version]bool, len(modFile.Exclude))
for _, x := range modFile.Exclude {
i.exclude[x.Mod] = true
}
return i
}
// modFileIsDirty reports whether the go.mod file differs meaningfully
// from what was indexed.
// If modFile has been changed (even cosmetically) since it was first read,
// modFile.Cleanup must be called before modFileIsDirty.
func (i *modFileIndex) modFileIsDirty(modFile *modfile.File) bool {
if i == nil {
return modFile != nil
}
if i.dataNeedsFix {
return true
}
if modFile.Module == nil {
if i.module != (module.Version{}) {
return true
}
} else if modFile.Module.Mod != i.module {
return true
}
if modFile.Go == nil {
if i.goVersion != "" {
return true
}
} else if modFile.Go.Version != i.goVersion {
if i.goVersion == "" && cfg.BuildMod == "readonly" {
// go.mod files did not always require a 'go' version, so do not error out
// if one is missing — we may be inside an older module in the module
// cache, and should bias toward providing useful behavior.
} else {
return true
}
}
if len(modFile.Require) != len(i.require) ||
len(modFile.Replace) != len(i.replace) ||
len(modFile.Exclude) != len(i.exclude) {
return true
}
for _, r := range modFile.Require {
if meta, ok := i.require[r.Mod]; !ok {
return true
} else if r.Indirect != meta.indirect {
if cfg.BuildMod == "readonly" {
// The module's requirements are consistent; only the "// indirect"
// comments that are wrong. But those are only guaranteed to be accurate
// after a "go mod tidy" — it's a good idea to run those before
// committing a change, but it's certainly not mandatory.
} else {
return true
}
}
}
for _, r := range modFile.Replace {
if r.New != i.replace[r.Old] {
return true
}
}
for _, x := range modFile.Exclude {
if !i.exclude[x.Mod] {
return true
}
}
return false
}

View file

@ -0,0 +1,259 @@
// Copyright 2020 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 modload
import (
"errors"
"fmt"
"os"
"path/filepath"
"sort"
"sync"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/lockedfile"
"cmd/go/internal/modfetch"
"cmd/go/internal/mvs"
"cmd/go/internal/par"
"golang.org/x/mod/modfile"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
)
// mvsReqs implements mvs.Reqs for module semantic versions,
// with any exclusions or replacements applied internally.
type mvsReqs struct {
buildList []module.Version
cache par.Cache
versions sync.Map
}
// Reqs returns the current module requirement graph.
// Future calls to SetBuildList do not affect the operation
// of the returned Reqs.
func Reqs() mvs.Reqs {
r := &mvsReqs{
buildList: buildList,
}
return r
}
func (r *mvsReqs) Required(mod module.Version) ([]module.Version, error) {
type cached struct {
list []module.Version
err error
}
c := r.cache.Do(mod, func() interface{} {
list, err := r.required(mod)
if err != nil {
return cached{nil, err}
}
for i, mv := range list {
if index != nil {
for index.exclude[mv] {
mv1, err := r.next(mv)
if err != nil {
return cached{nil, err}
}
if mv1.Version == "none" {
return cached{nil, fmt.Errorf("%s(%s) depends on excluded %s(%s) with no newer version available", mod.Path, mod.Version, mv.Path, mv.Version)}
}
mv = mv1
}
}
list[i] = mv
}
return cached{list, nil}
}).(cached)
return c.list, c.err
}
func (r *mvsReqs) modFileToList(f *modfile.File) []module.Version {
list := make([]module.Version, 0, len(f.Require))
for _, r := range f.Require {
list = append(list, r.Mod)
}
return list
}
// required returns a unique copy of the requirements of mod.
func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
if mod == Target {
if modFile != nil && modFile.Go != nil {
r.versions.LoadOrStore(mod, modFile.Go.Version)
}
return append([]module.Version(nil), r.buildList[1:]...), nil
}
if cfg.BuildMod == "vendor" {
// For every module other than the target,
// return the full list of modules from modules.txt.
readVendorList()
return append([]module.Version(nil), vendorList...), nil
}
origPath := mod.Path
if repl := Replacement(mod); repl.Path != "" {
if repl.Version == "" {
// TODO: need to slip the new version into the tags list etc.
dir := repl.Path
if !filepath.IsAbs(dir) {
dir = filepath.Join(ModRoot(), dir)
}
gomod := filepath.Join(dir, "go.mod")
data, err := lockedfile.Read(gomod)
if err != nil {
return nil, fmt.Errorf("parsing %s: %v", base.ShortPath(gomod), err)
}
f, err := modfile.ParseLax(gomod, data, nil)
if err != nil {
return nil, fmt.Errorf("parsing %s: %v", base.ShortPath(gomod), err)
}
if f.Go != nil {
r.versions.LoadOrStore(mod, f.Go.Version)
}
return r.modFileToList(f), nil
}
mod = repl
}
if mod.Version == "none" {
return nil, nil
}
if !semver.IsValid(mod.Version) {
// Disallow the broader queries supported by fetch.Lookup.
base.Fatalf("go: internal error: %s@%s: unexpected invalid semantic version", mod.Path, mod.Version)
}
data, err := modfetch.GoMod(mod.Path, mod.Version)
if err != nil {
return nil, err
}
f, err := modfile.ParseLax("go.mod", data, nil)
if err != nil {
return nil, module.VersionError(mod, fmt.Errorf("parsing go.mod: %v", err))
}
if f.Module == nil {
return nil, module.VersionError(mod, errors.New("parsing go.mod: missing module line"))
}
if mpath := f.Module.Mod.Path; mpath != origPath && mpath != mod.Path {
return nil, module.VersionError(mod, fmt.Errorf(`parsing go.mod:
module declares its path as: %s
but was required as: %s`, mpath, origPath))
}
if f.Go != nil {
r.versions.LoadOrStore(mod, f.Go.Version)
}
return r.modFileToList(f), nil
}
// Max returns the maximum of v1 and v2 according to semver.Compare.
//
// As a special case, the version "" is considered higher than all other
// versions. The main module (also known as the target) has no version and must
// be chosen over other versions of the same module in the module dependency
// graph.
func (*mvsReqs) Max(v1, v2 string) string {
if v1 != "" && semver.Compare(v1, v2) == -1 {
return v2
}
return v1
}
// Upgrade is a no-op, here to implement mvs.Reqs.
// The upgrade logic for go get -u is in ../modget/get.go.
func (*mvsReqs) Upgrade(m module.Version) (module.Version, error) {
return m, nil
}
func versions(path string) ([]string, error) {
// Note: modfetch.Lookup and repo.Versions are cached,
// so there's no need for us to add extra caching here.
var versions []string
err := modfetch.TryProxies(func(proxy string) error {
repo, err := modfetch.Lookup(proxy, path)
if err == nil {
versions, err = repo.Versions("")
}
return err
})
return versions, err
}
// Previous returns the tagged version of m.Path immediately prior to
// m.Version, or version "none" if no prior version is tagged.
func (*mvsReqs) Previous(m module.Version) (module.Version, error) {
list, err := versions(m.Path)
if err != nil {
return module.Version{}, err
}
i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) >= 0 })
if i > 0 {
return module.Version{Path: m.Path, Version: list[i-1]}, nil
}
return module.Version{Path: m.Path, Version: "none"}, nil
}
// next returns the next version of m.Path after m.Version.
// It is only used by the exclusion processing in the Required method,
// not called directly by MVS.
func (*mvsReqs) next(m module.Version) (module.Version, error) {
list, err := versions(m.Path)
if err != nil {
return module.Version{}, err
}
i := sort.Search(len(list), func(i int) bool { return semver.Compare(list[i], m.Version) > 0 })
if i < len(list) {
return module.Version{Path: m.Path, Version: list[i]}, nil
}
return module.Version{Path: m.Path, Version: "none"}, nil
}
// fetch downloads the given module (or its replacement)
// and returns its location.
//
// The isLocal return value reports whether the replacement,
// if any, is local to the filesystem.
func fetch(mod module.Version) (dir string, isLocal bool, err error) {
if mod == Target {
return ModRoot(), true, nil
}
if r := Replacement(mod); r.Path != "" {
if r.Version == "" {
dir = r.Path
if !filepath.IsAbs(dir) {
dir = filepath.Join(ModRoot(), dir)
}
// Ensure that the replacement directory actually exists:
// dirInModule does not report errors for missing modules,
// so if we don't report the error now, later failures will be
// very mysterious.
if _, err := os.Stat(dir); err != nil {
if os.IsNotExist(err) {
// Semantically the module version itself “exists” — we just don't
// have its source code. Remove the equivalence to os.ErrNotExist,
// and make the message more concise while we're at it.
err = fmt.Errorf("replacement directory %s does not exist", r.Path)
} else {
err = fmt.Errorf("replacement directory %s: %w", r.Path, err)
}
return dir, true, module.VersionError(mod, err)
}
return dir, true, nil
}
mod = r
}
dir, err = modfetch.Download(mod)
return dir, false, err
}

View file

@ -381,7 +381,8 @@ type QueryResult struct {
// module and only the version "latest", without checking for other possible
// modules.
func QueryPackage(path, query string, allowed func(module.Version) bool) ([]QueryResult, error) {
if search.IsMetaPackage(path) || strings.Contains(path, "...") {
m := search.NewMatch(path)
if m.IsLocal() || !m.IsLiteral() {
return nil, fmt.Errorf("pattern %s is not an importable package", path)
}
return QueryPattern(path, query, allowed)
@ -402,30 +403,42 @@ func QueryPackage(path, query string, allowed func(module.Version) bool) ([]Quer
// possible modules.
func QueryPattern(pattern, query string, allowed func(module.Version) bool) ([]QueryResult, error) {
base := pattern
var match func(m module.Version, root string, isLocal bool) (pkgs []string)
firstError := func(m *search.Match) error {
if len(m.Errs) == 0 {
return nil
}
return m.Errs[0]
}
var match func(mod module.Version, root string, isLocal bool) *search.Match
if i := strings.Index(pattern, "..."); i >= 0 {
base = pathpkg.Dir(pattern[:i+3])
match = func(m module.Version, root string, isLocal bool) []string {
return matchPackages(pattern, imports.AnyTags(), false, []module.Version{m})
match = func(mod module.Version, root string, isLocal bool) *search.Match {
m := search.NewMatch(pattern)
matchPackages(m, imports.AnyTags(), omitStd, []module.Version{mod})
return m
}
} else {
match = func(m module.Version, root string, isLocal bool) []string {
prefix := m.Path
if m == Target {
match = func(mod module.Version, root string, isLocal bool) *search.Match {
m := search.NewMatch(pattern)
prefix := mod.Path
if mod == Target {
prefix = targetPrefix
}
if _, ok := dirInModule(pattern, prefix, root, isLocal); ok {
return []string{pattern}
} else {
return nil
if _, ok, err := dirInModule(pattern, prefix, root, isLocal); err != nil {
m.AddError(err)
} else if ok {
m.Pkgs = []string{pattern}
}
return m
}
}
if HasModRoot() {
pkgs := match(Target, modRoot, true)
if len(pkgs) > 0 {
m := match(Target, modRoot, true)
if len(m.Pkgs) > 0 {
if query != "latest" {
return nil, fmt.Errorf("can't query specific version for package %s in the main module (%s)", pattern, Target.Path)
}
@ -435,9 +448,12 @@ func QueryPattern(pattern, query string, allowed func(module.Version) bool) ([]Q
return []QueryResult{{
Mod: Target,
Rev: &modfetch.RevInfo{Version: Target.Version},
Packages: pkgs,
Packages: m.Pkgs,
}}, nil
}
if err := firstError(m); err != nil {
return nil, err
}
}
var (
@ -445,13 +461,18 @@ func QueryPattern(pattern, query string, allowed func(module.Version) bool) ([]Q
candidateModules = modulePrefixesExcludingTarget(base)
)
if len(candidateModules) == 0 {
return nil, fmt.Errorf("package %s is not in the main module (%s)", pattern, Target.Path)
return nil, &PackageNotInModuleError{
Mod: Target,
Query: query,
Pattern: pattern,
}
}
err := modfetch.TryProxies(func(proxy string) error {
queryModule := func(path string) (r QueryResult, err error) {
current := findCurrentVersion(path)
r.Mod.Path = path
r.Rev, err = queryProxy(proxy, path, query, "", allowed)
r.Rev, err = queryProxy(proxy, path, query, current, allowed)
if err != nil {
return r, err
}
@ -460,8 +481,12 @@ func QueryPattern(pattern, query string, allowed func(module.Version) bool) ([]Q
if err != nil {
return r, err
}
r.Packages = match(r.Mod, root, isLocal)
m := match(r.Mod, root, isLocal)
r.Packages = m.Pkgs
if len(r.Packages) == 0 {
if err := firstError(m); err != nil {
return r, err
}
return r, &PackageNotInModuleError{
Mod: r.Mod,
Replacement: Replacement(r.Mod),
@ -503,6 +528,15 @@ func modulePrefixesExcludingTarget(path string) []string {
return prefixes
}
func findCurrentVersion(path string) string {
for _, m := range buildList {
if m.Path == path {
return m.Version
}
}
return ""
}
type prefixResult struct {
QueryResult
err error
@ -541,7 +575,9 @@ func queryPrefixModules(candidateModules []string, queryModule func(path string)
case nil:
found = append(found, r.QueryResult)
case *PackageNotInModuleError:
if noPackage == nil {
// Given the option, prefer to attribute “package not in module”
// to modules other than the main one.
if noPackage == nil || noPackage.Mod == Target {
noPackage = rErr
}
case *NoMatchingVersionError:
@ -626,6 +662,13 @@ type PackageNotInModuleError struct {
}
func (e *PackageNotInModuleError) Error() string {
if e.Mod == Target {
if strings.Contains(e.Pattern, "...") {
return fmt.Sprintf("main module (%s) does not contain packages matching %s", Target.Path, e.Pattern)
}
return fmt.Sprintf("main module (%s) does not contain package %s", Target.Path, e.Pattern)
}
found := ""
if r := e.Replacement; r.Path != "" {
replacement := r.Path
@ -647,14 +690,21 @@ func (e *PackageNotInModuleError) Error() string {
return fmt.Sprintf("module %s@%s found%s, but does not contain package %s", e.Mod.Path, e.Query, found, e.Pattern)
}
func (e *PackageNotInModuleError) ImportPath() string {
if !strings.Contains(e.Pattern, "...") {
return e.Pattern
}
return ""
}
// ModuleHasRootPackage returns whether module m contains a package m.Path.
func ModuleHasRootPackage(m module.Version) (bool, error) {
root, isLocal, err := fetch(m)
if err != nil {
return false, err
}
_, ok := dirInModule(m.Path, m.Path, root, isLocal)
return ok, nil
_, ok, err := dirInModule(m.Path, m.Path, root, isLocal)
return ok, err
}
func versionHasGoMod(m module.Version) (bool, error) {

View file

@ -15,8 +15,6 @@ import (
"testing"
"cmd/go/internal/cfg"
"cmd/go/internal/modfetch"
"cmd/go/internal/modfetch/codehost"
"golang.org/x/mod/module"
)
@ -36,8 +34,7 @@ func testMain(m *testing.M) int {
os.Setenv("GOPATH", dir)
cfg.BuildContext.GOPATH = dir
modfetch.PkgMod = filepath.Join(dir, "pkg/mod")
codehost.WorkRoot = filepath.Join(dir, "codework")
cfg.GOMODCACHE = filepath.Join(dir, "pkg/mod")
return m.Run()
}

View file

@ -8,9 +8,9 @@ import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/imports"
"cmd/go/internal/search"
@ -18,14 +18,24 @@ import (
"golang.org/x/mod/module"
)
// matchPackages returns a list of packages in the list of modules
// matching the pattern. Package loading assumes the given set of tags.
func matchPackages(pattern string, tags map[string]bool, useStd bool, modules []module.Version) []string {
match := func(string) bool { return true }
type stdFilter int8
const (
omitStd = stdFilter(iota)
includeStd
)
// matchPackages is like m.MatchPackages, but uses a local variable (rather than
// a global) for tags, can include or exclude packages in the standard library,
// and is restricted to the given list of modules.
func matchPackages(m *search.Match, tags map[string]bool, filter stdFilter, modules []module.Version) {
m.Pkgs = []string{}
isMatch := func(string) bool { return true }
treeCanMatch := func(string) bool { return true }
if !search.IsMetaPackage(pattern) {
match = search.MatchPattern(pattern)
treeCanMatch = search.TreeCanMatchPattern(pattern)
if !m.IsMeta() {
isMatch = search.MatchPattern(m.Pattern())
treeCanMatch = search.TreeCanMatchPattern(m.Pattern())
}
have := map[string]bool{
@ -34,7 +44,6 @@ func matchPackages(pattern string, tags map[string]bool, useStd bool, modules []
if !cfg.BuildContext.CgoEnabled {
have["runtime/cgo"] = true // ignore during walk
}
var pkgs []string
type pruning int8
const (
@ -44,8 +53,9 @@ func matchPackages(pattern string, tags map[string]bool, useStd bool, modules []
walkPkgs := func(root, importPathRoot string, prune pruning) {
root = filepath.Clean(root)
filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
if err != nil {
m.AddError(err)
return nil
}
@ -94,9 +104,9 @@ func matchPackages(pattern string, tags map[string]bool, useStd bool, modules []
if !have[name] {
have[name] = true
if match(name) {
if isMatch(name) {
if _, _, err := scanDir(path, tags); err != imports.ErrNoGo {
pkgs = append(pkgs, name)
m.Pkgs = append(m.Pkgs, name)
}
}
}
@ -106,9 +116,12 @@ func matchPackages(pattern string, tags map[string]bool, useStd bool, modules []
}
return nil
})
if err != nil {
m.AddError(err)
}
}
if useStd {
if filter == includeStd && runtime.Compiler != "gccgo" {
walkPkgs(cfg.GOROOTsrc, "", pruneGoMod)
if treeCanMatch("cmd") {
walkPkgs(filepath.Join(cfg.GOROOTsrc, "cmd"), "cmd", pruneGoMod)
@ -120,7 +133,7 @@ func matchPackages(pattern string, tags map[string]bool, useStd bool, modules []
walkPkgs(ModRoot(), targetPrefix, pruneGoMod|pruneVendor)
walkPkgs(filepath.Join(ModRoot(), "vendor"), "", pruneVendor)
}
return pkgs
return
}
for _, mod := range modules {
@ -143,7 +156,7 @@ func matchPackages(pattern string, tags map[string]bool, useStd bool, modules []
var err error
root, isLocal, err = fetch(mod)
if err != nil {
base.Errorf("go: %v", err)
m.AddError(err)
continue
}
modPrefix = mod.Path
@ -156,5 +169,5 @@ func matchPackages(pattern string, tags map[string]bool, useStd bool, modules []
walkPkgs(root, modPrefix, prune)
}
return pkgs
return
}

View file

@ -0,0 +1,217 @@
// Copyright 2020 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 modload
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
"cmd/go/internal/base"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
)
var (
vendorOnce sync.Once
vendorList []module.Version // modules that contribute packages to the build, in order of appearance
vendorReplaced []module.Version // all replaced modules; may or may not also contribute packages
vendorVersion map[string]string // module path → selected version (if known)
vendorPkgModule map[string]module.Version // package → containing module
vendorMeta map[module.Version]vendorMetadata
)
type vendorMetadata struct {
Explicit bool
Replacement module.Version
}
// readVendorList reads the list of vendored modules from vendor/modules.txt.
func readVendorList() {
vendorOnce.Do(func() {
vendorList = nil
vendorPkgModule = make(map[string]module.Version)
vendorVersion = make(map[string]string)
vendorMeta = make(map[module.Version]vendorMetadata)
data, err := ioutil.ReadFile(filepath.Join(ModRoot(), "vendor/modules.txt"))
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
base.Fatalf("go: %s", err)
}
return
}
var mod module.Version
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, "# ") {
f := strings.Fields(line)
if len(f) < 3 {
continue
}
if semver.IsValid(f[2]) {
// A module, but we don't yet know whether it is in the build list or
// only included to indicate a replacement.
mod = module.Version{Path: f[1], Version: f[2]}
f = f[3:]
} else if f[2] == "=>" {
// A wildcard replacement found in the main module's go.mod file.
mod = module.Version{Path: f[1]}
f = f[2:]
} else {
// Not a version or a wildcard replacement.
// We don't know how to interpret this module line, so ignore it.
mod = module.Version{}
continue
}
if len(f) >= 2 && f[0] == "=>" {
meta := vendorMeta[mod]
if len(f) == 2 {
// File replacement.
meta.Replacement = module.Version{Path: f[1]}
vendorReplaced = append(vendorReplaced, mod)
} else if len(f) == 3 && semver.IsValid(f[2]) {
// Path and version replacement.
meta.Replacement = module.Version{Path: f[1], Version: f[2]}
vendorReplaced = append(vendorReplaced, mod)
} else {
// We don't understand this replacement. Ignore it.
}
vendorMeta[mod] = meta
}
continue
}
// Not a module line. Must be a package within a module or a metadata
// directive, either of which requires a preceding module line.
if mod.Path == "" {
continue
}
if strings.HasPrefix(line, "## ") {
// Metadata. Take the union of annotations across multiple lines, if present.
meta := vendorMeta[mod]
for _, entry := range strings.Split(strings.TrimPrefix(line, "## "), ";") {
entry = strings.TrimSpace(entry)
if entry == "explicit" {
meta.Explicit = true
}
// All other tokens are reserved for future use.
}
vendorMeta[mod] = meta
continue
}
if f := strings.Fields(line); len(f) == 1 && module.CheckImportPath(f[0]) == nil {
// A package within the current module.
vendorPkgModule[f[0]] = mod
// Since this module provides a package for the build, we know that it
// is in the build list and is the selected version of its path.
// If this information is new, record it.
if v, ok := vendorVersion[mod.Path]; !ok || semver.Compare(v, mod.Version) < 0 {
vendorList = append(vendorList, mod)
vendorVersion[mod.Path] = mod.Version
}
}
}
})
}
// checkVendorConsistency verifies that the vendor/modules.txt file matches (if
// go 1.14) or at least does not contradict (go 1.13 or earlier) the
// requirements and replacements listed in the main module's go.mod file.
func checkVendorConsistency() {
readVendorList()
pre114 := false
if modFile.Go == nil || semver.Compare("v"+modFile.Go.Version, "v1.14") < 0 {
// Go versions before 1.14 did not include enough information in
// vendor/modules.txt to check for consistency.
// If we know that we're on an earlier version, relax the consistency check.
pre114 = true
}
vendErrors := new(strings.Builder)
vendErrorf := func(mod module.Version, format string, args ...interface{}) {
detail := fmt.Sprintf(format, args...)
if mod.Version == "" {
fmt.Fprintf(vendErrors, "\n\t%s: %s", mod.Path, detail)
} else {
fmt.Fprintf(vendErrors, "\n\t%s@%s: %s", mod.Path, mod.Version, detail)
}
}
for _, r := range modFile.Require {
if !vendorMeta[r.Mod].Explicit {
if pre114 {
// Before 1.14, modules.txt did not indicate whether modules were listed
// explicitly in the main module's go.mod file.
// However, we can at least detect a version mismatch if packages were
// vendored from a non-matching version.
if vv, ok := vendorVersion[r.Mod.Path]; ok && vv != r.Mod.Version {
vendErrorf(r.Mod, fmt.Sprintf("is explicitly required in go.mod, but vendor/modules.txt indicates %s@%s", r.Mod.Path, vv))
}
} else {
vendErrorf(r.Mod, "is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt")
}
}
}
describe := func(m module.Version) string {
if m.Version == "" {
return m.Path
}
return m.Path + "@" + m.Version
}
// We need to verify *all* replacements that occur in modfile: even if they
// don't directly apply to any module in the vendor list, the replacement
// go.mod file can affect the selected versions of other (transitive)
// dependencies
for _, r := range modFile.Replace {
vr := vendorMeta[r.Old].Replacement
if vr == (module.Version{}) {
if pre114 && (r.Old.Version == "" || vendorVersion[r.Old.Path] != r.Old.Version) {
// Before 1.14, modules.txt omitted wildcard replacements and
// replacements for modules that did not have any packages to vendor.
} else {
vendErrorf(r.Old, "is replaced in go.mod, but not marked as replaced in vendor/modules.txt")
}
} else if vr != r.New {
vendErrorf(r.Old, "is replaced by %s in go.mod, but marked as replaced by %s in vendor/modules.txt", describe(r.New), describe(vr))
}
}
for _, mod := range vendorList {
meta := vendorMeta[mod]
if meta.Explicit {
if _, inGoMod := index.require[mod]; !inGoMod {
vendErrorf(mod, "is marked as explicit in vendor/modules.txt, but not explicitly required in go.mod")
}
}
}
for _, mod := range vendorReplaced {
r := Replacement(mod)
if r == (module.Version{}) {
vendErrorf(mod, "is marked as replaced in vendor/modules.txt, but not replaced in go.mod")
continue
}
if meta := vendorMeta[mod]; r != meta.Replacement {
vendErrorf(mod, "is marked as replaced by %s in vendor/modules.txt, but replaced by %s in go.mod", describe(meta.Replacement), describe(r))
}
}
if vendErrors.Len() > 0 {
base.Fatalf("go: inconsistent vendoring in %s:%s\n\nrun 'go mod vendor' to sync, or use -mod=mod or -mod=readonly to ignore the vendor directory", modRoot, vendErrors)
}
}

View file

@ -115,7 +115,21 @@ func (e *BuildListError) Error() string {
}
// BuildList returns the build list for the target module.
// The first element is the target itself, with the remainder of the list sorted by path.
//
// target is the root vertex of a module requirement graph. For cmd/go, this is
// typically the main module, but note that this algorithm is not intended to
// be Go-specific: module paths and versions are treated as opaque values.
//
// reqs describes the module requirement graph and provides an opaque method
// for comparing versions.
//
// BuildList traverses the graph and returns a list containing the highest
// version for each visited module. The first element of the returned list is
// target itself; reqs.Max requires target.Version to compare higher than all
// other versions, so no other version can be selected. The remaining elements
// of the list are sorted by path.
//
// See https://research.swtch.com/vgo-mvs for details.
func BuildList(target module.Version, reqs Reqs) ([]module.Version, error) {
return buildList(target, reqs, nil)
}
@ -220,10 +234,9 @@ func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) (m
// The final list is the minimum version of each module found in the graph.
if v := min[target.Path]; v != target.Version {
// TODO(jayconrod): there is a special case in modload.mvsReqs.Max
// that prevents us from selecting a newer version of a module
// when the module has no version. This may only be the case for target.
// Should we always panic when target has a version?
// target.Version will be "" for modload, the main client of MVS.
// "" denotes the main module, which has no version. However, MVS treats
// version strings as opaque, so "" is not a special value here.
// See golang.org/issue/31491, golang.org/issue/29773.
panic(fmt.Sprintf("mistake: chose version %q instead of target %+v", v, target)) // TODO: Don't panic.
}

View file

@ -9,11 +9,13 @@ package renameio
import (
"encoding/binary"
"errors"
"internal/testenv"
"io/ioutil"
"math/rand"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"sync/atomic"
"syscall"
@ -24,6 +26,10 @@ import (
)
func TestConcurrentReadsAndWrites(t *testing.T) {
if runtime.GOOS == "darwin" && strings.HasSuffix(testenv.Builder(), "-10_14") {
testenv.SkipFlaky(t, 33041)
}
dir, err := ioutil.TempDir("", "renameio")
if err != nil {
t.Fatal(err)

View file

@ -9,7 +9,6 @@ import (
"cmd/go/internal/cfg"
"fmt"
"go/build"
"log"
"os"
"path"
"path/filepath"
@ -19,25 +18,97 @@ import (
// A Match represents the result of matching a single package pattern.
type Match struct {
Pattern string // the pattern itself
Literal bool // whether it is a literal (no wildcards)
Pkgs []string // matching packages (dirs or import paths)
pattern string // the pattern itself
Dirs []string // if the pattern is local, directories that potentially contain matching packages
Pkgs []string // matching packages (import paths)
Errs []error // errors matching the patterns to packages, NOT errors loading those packages
// Errs may be non-empty even if len(Pkgs) > 0, indicating that some matching
// packages could be located but results may be incomplete.
// If len(Pkgs) == 0 && len(Errs) == 0, the pattern is well-formed but did not
// match any packages.
}
// MatchPackages 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 MatchPackages(pattern string) *Match {
m := &Match{
Pattern: pattern,
Literal: false,
// NewMatch returns a Match describing the given pattern,
// without resolving its packages or errors.
func NewMatch(pattern string) *Match {
return &Match{pattern: pattern}
}
// Pattern returns the pattern to be matched.
func (m *Match) Pattern() string { return m.pattern }
// AddError appends a MatchError wrapping err to m.Errs.
func (m *Match) AddError(err error) {
m.Errs = append(m.Errs, &MatchError{Match: m, Err: err})
}
// Literal reports whether the pattern is free of wildcards and meta-patterns.
//
// A literal pattern must match at most one package.
func (m *Match) IsLiteral() bool {
return !strings.Contains(m.pattern, "...") && !m.IsMeta()
}
// Local reports whether the pattern must be resolved from a specific root or
// directory, such as a filesystem path or a single module.
func (m *Match) IsLocal() bool {
return build.IsLocalImport(m.pattern) || filepath.IsAbs(m.pattern)
}
// Meta reports whether the pattern is a “meta-package” keyword that represents
// multiple packages, such as "std", "cmd", or "all".
func (m *Match) IsMeta() bool {
return IsMetaPackage(m.pattern)
}
// 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"
}
// A MatchError indicates an error that occurred while attempting to match a
// pattern.
type MatchError struct {
Match *Match
Err error
}
func (e *MatchError) Error() string {
if e.Match.IsLiteral() {
return fmt.Sprintf("%s: %v", e.Match.Pattern(), e.Err)
}
return fmt.Sprintf("pattern %s: %v", e.Match.Pattern(), e.Err)
}
func (e *MatchError) Unwrap() error {
return e.Err
}
// MatchPackages sets m.Pkgs to a non-nil slice containing all the packages that
// can be found under the $GOPATH directories and $GOROOT that match the
// pattern. The pattern must be either "all" (all packages), "std" (standard
// packages), "cmd" (standard commands), or a path including "...".
//
// If any errors may have caused the set of packages to be incomplete,
// MatchPackages appends those errors to m.Errs.
func (m *Match) MatchPackages() {
m.Pkgs = []string{}
if m.IsLocal() {
m.AddError(fmt.Errorf("internal error: MatchPackages: %s is not a valid package pattern", m.pattern))
return
}
if m.IsLiteral() {
m.Pkgs = []string{m.pattern}
return
}
match := func(string) bool { return true }
treeCanMatch := func(string) bool { return true }
if !IsMetaPackage(pattern) {
match = MatchPattern(pattern)
treeCanMatch = TreeCanMatchPattern(pattern)
if !m.IsMeta() {
match = MatchPattern(m.pattern)
treeCanMatch = TreeCanMatchPattern(m.pattern)
}
have := map[string]bool{
@ -48,17 +119,20 @@ func MatchPackages(pattern string) *Match {
}
for _, src := range cfg.BuildContext.SrcDirs() {
if (pattern == "std" || pattern == "cmd") && src != cfg.GOROOTsrc {
if (m.pattern == "std" || m.pattern == "cmd") && src != cfg.GOROOTsrc {
continue
}
src = filepath.Clean(src) + string(filepath.Separator)
root := src
if pattern == "cmd" {
if m.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
err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
if err != nil {
return err // Likely a permission error, which could interfere with matching.
}
if path == src {
return nil // GOROOT/src and GOPATH/src cannot contain packages.
}
want := true
@ -69,7 +143,7 @@ func MatchPackages(pattern string) *Match {
}
name := filepath.ToSlash(path[len(src):])
if pattern == "std" && (!IsStandardImportPath(name) || name == "cmd") {
if m.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
@ -100,23 +174,30 @@ func MatchPackages(pattern string) *Match {
pkg, err := cfg.BuildContext.ImportDir(path, 0)
if err != nil {
if _, noGo := err.(*build.NoGoError); noGo {
// The package does not actually exist, so record neither the package
// nor the error.
return nil
}
// There was an error importing path, but not matching it,
// which is all that Match promises to do.
// Ignore the import error.
}
// 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" {
if m.pattern == "cmd" && pkg != nil && strings.HasPrefix(pkg.ImportPath, "cmd/vendor") && pkg.Name == "main" {
return nil
}
m.Pkgs = append(m.Pkgs, name)
return nil
})
if err != nil {
m.AddError(err)
}
}
return m
}
var modRoot string
@ -125,24 +206,31 @@ func SetModRoot(dir string) {
modRoot = dir
}
// MatchPackagesInFS is like MatchPackages but is passed a pattern that
// begins with an absolute path or "./" or "../". On Windows, the pattern may
// use slash or backslash separators or a mix of both.
// MatchDirs sets m.Dirs to a non-nil slice containing all directories that
// potentially match a local pattern. The pattern must begin with an absolute
// path, or "./", or "../". On Windows, the pattern may use slash or backslash
// separators or a mix of both.
//
// MatchPackagesInFS scans the tree rooted at the directory that contains the
// first "..." wildcard and returns a match with packages that
func MatchPackagesInFS(pattern string) *Match {
m := &Match{
Pattern: pattern,
Literal: false,
// If any errors may have caused the set of directories to be incomplete,
// MatchDirs appends those errors to m.Errs.
func (m *Match) MatchDirs() {
m.Dirs = []string{}
if !m.IsLocal() {
m.AddError(fmt.Errorf("internal error: MatchDirs: %s is not a valid filesystem pattern", m.pattern))
return
}
if m.IsLiteral() {
m.Dirs = []string{m.pattern}
return
}
// Clean the path and create a matching predicate.
// filepath.Clean removes "./" prefixes (and ".\" on Windows). We need to
// preserve these, since they are meaningful in MatchPattern and in
// returned import paths.
cleanPattern := filepath.Clean(pattern)
isLocal := strings.HasPrefix(pattern, "./") || (os.PathSeparator == '\\' && strings.HasPrefix(pattern, `.\`))
cleanPattern := filepath.Clean(m.pattern)
isLocal := strings.HasPrefix(m.pattern, "./") || (os.PathSeparator == '\\' && strings.HasPrefix(m.pattern, `.\`))
prefix := ""
if cleanPattern != "." && isLocal {
prefix = "./"
@ -166,16 +254,20 @@ func MatchPackagesInFS(pattern string) *Match {
if modRoot != "" {
abs, err := filepath.Abs(dir)
if err != nil {
base.Fatalf("go: %v", err)
m.AddError(err)
return
}
if !hasFilepathPrefix(abs, modRoot) {
base.Fatalf("go: pattern %s refers to dir %s, outside module root %s", pattern, abs, modRoot)
return nil
m.AddError(fmt.Errorf("directory %s is outside module root (%s)", abs, modRoot))
return
}
}
filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
if err != nil || !fi.IsDir() {
err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
if err != nil {
return err // Likely a permission error, which could interfere with matching.
}
if !fi.IsDir() {
return nil
}
top := false
@ -218,15 +310,21 @@ func MatchPackagesInFS(pattern string) *Match {
// 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)
if _, noGo := err.(*build.NoGoError); noGo {
// The package does not actually exist, so record neither the package
// nor the error.
return nil
}
return nil
// There was an error importing path, but not matching it,
// which is all that Match promises to do.
// Ignore the import error.
}
m.Pkgs = append(m.Pkgs, name)
m.Dirs = append(m.Dirs, name)
return nil
})
return m
if err != nil {
m.AddError(err)
}
}
// TreeCanMatchPattern(pattern)(name) reports whether
@ -316,8 +414,8 @@ func replaceVendor(x, repl string) string {
// WarnUnmatched warns about patterns that didn't match any packages.
func WarnUnmatched(matches []*Match) {
for _, m := range matches {
if len(m.Pkgs) == 0 {
fmt.Fprintf(os.Stderr, "go: warning: %q matched no packages\n", m.Pattern)
if len(m.Pkgs) == 0 && len(m.Errs) == 0 {
fmt.Fprintf(os.Stderr, "go: warning: %q matched no packages\n", m.pattern)
}
}
}
@ -334,40 +432,30 @@ func ImportPaths(patterns []string) []*Match {
func ImportPathsQuiet(patterns []string) []*Match {
var out []*Match
for _, a := range CleanPatterns(patterns) {
if IsMetaPackage(a) {
out = append(out, MatchPackages(a))
continue
}
if build.IsLocalImport(a) || filepath.IsAbs(a) {
var m *Match
if strings.Contains(a, "...") {
m = MatchPackagesInFS(a)
} else {
m = &Match{Pattern: a, Literal: true, Pkgs: []string{a}}
}
m := NewMatch(a)
if m.IsLocal() {
m.MatchDirs()
// Change the file import path to a regular import path if the package
// is in GOPATH or GOROOT. We don't report errors here; LoadImport
// (or something similar) will report them later.
for i, dir := range m.Pkgs {
m.Pkgs = make([]string, len(m.Dirs))
for i, dir := range m.Dirs {
absDir := dir
if !filepath.IsAbs(dir) {
dir = filepath.Join(base.Cwd, dir)
absDir = filepath.Join(base.Cwd, dir)
}
if bp, _ := cfg.BuildContext.ImportDir(dir, build.FindOnly); bp.ImportPath != "" && bp.ImportPath != "." {
if bp, _ := cfg.BuildContext.ImportDir(absDir, build.FindOnly); bp.ImportPath != "" && bp.ImportPath != "." {
m.Pkgs[i] = bp.ImportPath
} else {
m.Pkgs[i] = dir
}
}
out = append(out, m)
continue
} else {
m.MatchPackages()
}
if strings.Contains(a, "...") {
out = append(out, MatchPackages(a))
continue
}
out = append(out, &Match{Pattern: a, Literal: true, Pkgs: []string{a}})
out = append(out, m)
}
return out
}
@ -419,11 +507,6 @@ func CleanPatterns(patterns []string) []string {
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"
}
// hasPathPrefix reports whether the path s begins with the
// elements in prefix.
func hasPathPrefix(s, prefix string) bool {

View file

@ -10,7 +10,7 @@ import (
"strings"
)
// HasPath reports whether the slash-separated path s
// HasPathPrefix reports whether the slash-separated path s
// begins with the elements in prefix.
func HasPathPrefix(s, prefix string) bool {
if len(s) == len(prefix) {

View file

@ -0,0 +1,34 @@
// Copyright 2019 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.
// Code generated by genflags.go — DO NOT EDIT.
package test
// passFlagToTest contains the flags that should be forwarded to
// the test binary with the prefix "test.".
var passFlagToTest = map[string]bool{
"bench": true,
"benchmem": true,
"benchtime": true,
"blockprofile": true,
"blockprofilerate": true,
"count": true,
"coverprofile": true,
"cpu": true,
"cpuprofile": true,
"failfast": true,
"list": true,
"memprofile": true,
"memprofilerate": true,
"mutexprofile": true,
"mutexprofilefraction": true,
"outputdir": true,
"parallel": true,
"run": true,
"short": true,
"timeout": true,
"trace": true,
"v": true,
}

View file

@ -0,0 +1,34 @@
// Copyright 2019 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 test
import (
"flag"
"strings"
"testing"
)
func TestPassFlagToTestIncludesAllTestFlags(t *testing.T) {
flag.VisitAll(func(f *flag.Flag) {
if !strings.HasPrefix(f.Name, "test.") {
return
}
name := strings.TrimPrefix(f.Name, "test.")
if name != "testlogfile" && !passFlagToTest[name] {
t.Errorf("passFlagToTest missing entry for %q (flag test.%s)", name, name)
t.Logf("(Run 'go generate cmd/go/internal/test' if it should be added.)")
}
})
for name := range passFlagToTest {
if flag.Lookup("test."+name) == nil {
t.Errorf("passFlagToTest contains %q, but flag -test.%s does not exist in test binary", name, name)
}
if CmdTest.Flag.Lookup(name) == nil {
t.Errorf("passFlagToTest contains %q, but flag -%s does not exist in 'go test' subcommand", name, name)
}
}
}

View file

@ -0,0 +1,90 @@
// Copyright 2019 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 main
import (
"bytes"
"flag"
"log"
"os"
"os/exec"
"strings"
"testing"
"text/template"
)
func main() {
if err := regenerate(); err != nil {
log.Fatal(err)
}
}
func regenerate() error {
t := template.Must(template.New("fileTemplate").Parse(fileTemplate))
buf := bytes.NewBuffer(nil)
if err := t.Execute(buf, testFlags()); err != nil {
return err
}
f, err := os.Create("flagdefs.go")
if err != nil {
return err
}
cmd := exec.Command("gofmt")
cmd.Stdin = buf
cmd.Stdout = f
cmd.Stderr = os.Stderr
cmdErr := cmd.Run()
if err := f.Close(); err != nil {
return err
}
if cmdErr != nil {
os.Remove(f.Name())
return cmdErr
}
return nil
}
func testFlags() []string {
testing.Init()
var names []string
flag.VisitAll(func(f *flag.Flag) {
if !strings.HasPrefix(f.Name, "test.") {
return
}
name := strings.TrimPrefix(f.Name, "test.")
if name == "testlogfile" {
// test.testlogfile is “for use only by cmd/go”
} else {
names = append(names, name)
}
})
return names
}
const fileTemplate = `// Copyright 2019 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.
// Code generated by genflags.go — DO NOT EDIT.
package test
// passFlagToTest contains the flags that should be forwarded to
// the test binary with the prefix "test.".
var passFlagToTest = map[string]bool {
{{- range .}}
"{{.}}": true,
{{- end }}
}
`

View file

@ -73,10 +73,10 @@ As part of building a test binary, go test runs go vet on the package
and its test source files to identify significant problems. If go vet
finds any problems, go test reports those and does not run the test
binary. Only a high-confidence subset of the default go vet checks are
used. That subset is: 'atomic', 'bool', 'buildtags', 'nilfunc', and
'printf'. You can see the documentation for these and other vet tests
via "go doc cmd/vet". To disable the running of go vet, use the
-vet=off flag.
used. That subset is: 'atomic', 'bool', 'buildtags', 'errorsas',
'ifaceassert', 'nilfunc', 'printf', and 'stringintconv'. You can see
the documentation for these and other vet tests via "go doc cmd/vet".
To disable the running of go vet, use the -vet=off flag.
All test output and summary lines are printed to the go command's
standard output, even if the test printed them to its own standard
@ -466,37 +466,78 @@ See the documentation of the testing package for more information.
}
var (
testC bool // -c flag
testCover bool // -cover flag
testCoverMode string // -covermode flag
testCoverPaths []string // -coverpkg flag
testCoverPkgs []*load.Package // -coverpkg flag
testCoverProfile string // -coverprofile flag
testOutputDir string // -outputdir flag
testO string // -o flag
testProfile string // profiling flag that limits test to one package
testNeedBinary bool // profile needs to keep binary around
testJSON bool // -json flag
testV bool // -v flag
testTimeout string // -timeout flag
testArgs []string
testBench bool
testList bool
testShowPass bool // show passing output
testVetList string // -vet flag
pkgArgs []string
pkgs []*load.Package
testActualTimeout = 10 * time.Minute // actual timeout which is passed to tests
testKillTimeout = testActualTimeout + 1*time.Minute // backup alarm
testCacheExpire time.Time // ignore cached test results before this time
testBench string // -bench flag
testC bool // -c flag
testCover bool // -cover flag
testCoverMode string // -covermode flag
testCoverPaths []string // -coverpkg flag
testCoverPkgs []*load.Package // -coverpkg flag
testCoverProfile string // -coverprofile flag
testJSON bool // -json flag
testList string // -list flag
testO string // -o flag
testOutputDir = base.Cwd // -outputdir flag
testTimeout time.Duration // -timeout flag
testV bool // -v flag
testVet = vetFlag{flags: defaultVetFlags} // -vet flag
)
// testVetExplicit records whether testVetFlags were set by an explicit -vet.
var testVetExplicit = false
var (
testArgs []string
pkgArgs []string
pkgs []*load.Package
// testVetFlags is the list of flags to pass to vet when invoked automatically during go test.
var testVetFlags = []string{
testHelp bool // -help option passed to test via -args
testKillTimeout = 100 * 365 * 24 * time.Hour // backup alarm; defaults to about a century if no timeout is set
testCacheExpire time.Time // ignore cached test results before this time
testBlockProfile, testCPUProfile, testMemProfile, testMutexProfile, testTrace string // profiling flag that limits test to one package
)
// testProfile returns the name of an arbitrary single-package profiling flag
// that is set, if any.
func testProfile() string {
switch {
case testBlockProfile != "":
return "-blockprofile"
case testCPUProfile != "":
return "-cpuprofile"
case testMemProfile != "":
return "-memprofile"
case testMutexProfile != "":
return "-mutexprofile"
case testTrace != "":
return "-trace"
default:
return ""
}
}
// testNeedBinary reports whether the test needs to keep the binary around.
func testNeedBinary() bool {
switch {
case testBlockProfile != "":
return true
case testCPUProfile != "":
return true
case testMemProfile != "":
return true
case testMutexProfile != "":
return true
case testO != "":
return true
default:
return false
}
}
// testShowPass reports whether the output for a passing test should be shown.
func testShowPass() bool {
return testV || (testList != "") || testHelp
}
var defaultVetFlags = []string{
// TODO(rsc): Decide which tests are enabled by default.
// See golang.org/issue/18085.
// "-asmdecl",
@ -509,12 +550,14 @@ var testVetFlags = []string{
// "-copylocks",
"-errorsas",
// "-httpresponse",
"-ifaceassert",
// "-lostcancel",
// "-methods",
"-nilfunc",
"-printf",
// "-rangeloops",
// "-shift",
"-stringintconv",
// "-structtags",
// "-tests",
// "-unreachable",
@ -522,22 +565,16 @@ var testVetFlags = []string{
// "-unusedresult",
}
func testCmdUsage() {
fmt.Fprintf(os.Stderr, "usage: %s\n", CmdTest.UsageLine)
fmt.Fprintf(os.Stderr, "Run 'go help %s' and 'go help %s' for details.\n", CmdTest.LongName(), HelpTestflag.LongName())
os.Exit(2)
}
func runTest(cmd *base.Command, args []string) {
modload.LoadTests = true
pkgArgs, testArgs = testFlags(testCmdUsage, args)
pkgArgs, testArgs = testFlags(args)
work.FindExecCmd() // initialize cached result
work.BuildInit()
work.VetFlags = testVetFlags
work.VetExplicit = testVetExplicit
work.VetFlags = testVet.flags
work.VetExplicit = testVet.explicit
pkgs = load.PackagesForBuild(pkgArgs)
if len(pkgs) == 0 {
@ -550,38 +587,20 @@ func runTest(cmd *base.Command, args []string) {
if testO != "" && len(pkgs) != 1 {
base.Fatalf("cannot use -o flag with multiple packages")
}
if testProfile != "" && len(pkgs) != 1 {
base.Fatalf("cannot use %s flag with multiple packages", testProfile)
if testProfile() != "" && len(pkgs) != 1 {
base.Fatalf("cannot use %s flag with multiple packages", testProfile())
}
initCoverProfile()
defer closeCoverProfile()
// If a test timeout was given and is parseable, set our kill timeout
// If a test timeout is finite, set our kill timeout
// to that timeout plus one minute. This is a backup alarm in case
// the test wedges with a goroutine spinning and its background
// timer does not get a chance to fire.
if dt, err := time.ParseDuration(testTimeout); err == nil && dt > 0 {
testActualTimeout = dt
testKillTimeout = testActualTimeout + 1*time.Minute
} else if err == nil && dt == 0 {
// An explicit zero disables the test timeout.
// No timeout is passed to tests.
// Let it have one century (almost) before we kill it.
testActualTimeout = -1
testKillTimeout = 100 * 365 * 24 * time.Hour
if testTimeout > 0 {
testKillTimeout = testTimeout + 1*time.Minute
}
// Pass timeout to tests if it exists.
// Prepend rather than appending so that it appears before positional arguments.
if testActualTimeout > 0 {
testArgs = append([]string{"-test.timeout=" + testActualTimeout.String()}, testArgs...)
}
// show passing test output (after buffering) with -v flag.
// must buffer because tests are running in parallel, and
// otherwise the output will get mixed.
testShowPass = testV || testList
// For 'go test -i -o x.test', we want to build x.test. Imply -c to make the logic easier.
if cfg.BuildI && testO != "" {
testC = true
@ -755,7 +774,7 @@ func runTest(cmd *base.Command, args []string) {
}
// Force benchmarks to run in serial.
if !testC && testBench {
if !testC && (testBench != "") {
// The first run must wait for all builds.
// Later runs must wait for the previous run's print.
for i, run := range runs {
@ -839,7 +858,7 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
}
pmain.Dir = testDir
pmain.Internal.OmitDebug = !testC && !testNeedBinary
pmain.Internal.OmitDebug = !testC && !testNeedBinary()
if !cfg.BuildN {
// writeTestmain writes _testmain.go,
@ -887,7 +906,7 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
}
buildAction = a
var installAction, cleanAction *work.Action
if testC || testNeedBinary {
if testC || testNeedBinary() {
// -c or profiling flag: create action to copy binary to ./test.out.
target := filepath.Join(base.Cwd, testBinary+cfg.ExeSuffix)
if testO != "" {
@ -964,7 +983,7 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
}
func addTestVet(b *work.Builder, p *load.Package, runAction, installAction *work.Action) {
if testVetList == "off" {
if testVet.off {
return
}
@ -1067,7 +1086,7 @@ func (c *runCache) builderRunTest(b *work.Builder, a *work.Action) error {
}
var buf bytes.Buffer
if len(pkgArgs) == 0 || testBench {
if len(pkgArgs) == 0 || (testBench != "") {
// Stream test output (no buffering) when no package has
// been given on the command line (implicit current directory)
// or when benchmarking.
@ -1087,7 +1106,7 @@ func (c *runCache) builderRunTest(b *work.Builder, a *work.Action) error {
// possible even when multiple tests are being run: the JSON output
// events are attributed to specific package tests, so interlacing them
// is OK.
if testShowPass && (len(pkgs) == 1 || cfg.BuildP == 1) || testJSON {
if testShowPass() && (len(pkgs) == 1 || cfg.BuildP == 1) || testJSON {
// Write both to stdout and buf, for possible saving
// to cache, and for looking for the "no tests to run" message.
stdout = io.MultiWriter(stdout, &buf)
@ -1142,7 +1161,7 @@ func (c *runCache) builderRunTest(b *work.Builder, a *work.Action) error {
cmd := exec.Command(args[0], args[1:]...)
cmd.Dir = a.Package.Dir
cmd.Env = base.EnvForDir(cmd.Dir, cfg.OrigEnv[:len(cfg.OrigEnv):len(cfg.OrigEnv)])
cmd.Env = base.AppendPWD(cfg.OrigEnv[:len(cfg.OrigEnv):len(cfg.OrigEnv)], cmd.Dir)
cmd.Stdout = stdout
cmd.Stderr = stdout
@ -1209,7 +1228,7 @@ func (c *runCache) builderRunTest(b *work.Builder, a *work.Action) error {
if err == nil {
norun := ""
if !testShowPass && !testJSON {
if !testShowPass() && !testJSON {
buf.Reset()
}
if bytes.HasPrefix(out, noTestsToRun[1:]) || bytes.Contains(out, noTestsToRun) {
@ -1284,16 +1303,13 @@ func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bo
"-test.parallel",
"-test.run",
"-test.short",
"-test.timeout",
"-test.v":
// These are cacheable.
// Note that this list is documented above,
// so if you add to this list, update the docs too.
cacheArgs = append(cacheArgs, arg)
case "-test.timeout":
// Special case: this is cacheable but ignored during the hash.
// Do not add to cacheArgs.
default:
// nothing else is cacheable
if cache.DebugTest {

View file

@ -5,81 +5,197 @@
package test
import (
"errors"
"flag"
"fmt"
"os"
"path/filepath"
"strings"
"time"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/cmdflag"
"cmd/go/internal/str"
"cmd/go/internal/work"
)
const cmd = "test"
//go:generate go run ./genflags.go
// The flag handling part of go test is large and distracting.
// We can't use the flag package because some of the flags from
// our command line are for us, and some are for 6.out, and
// We can't use (*flag.FlagSet).Parse because some of the flags from
// our command line are for us, and some are for the test binary, and
// some are for both.
// testFlagDefn is the set of flags we process.
var testFlagDefn = []*cmdflag.Defn{
// local.
{Name: "c", BoolVar: &testC},
{Name: "i", BoolVar: &cfg.BuildI},
{Name: "o"},
{Name: "cover", BoolVar: &testCover},
{Name: "covermode"},
{Name: "coverpkg"},
{Name: "exec"},
{Name: "json", BoolVar: &testJSON},
{Name: "vet"},
func init() {
work.AddBuildFlags(CmdTest, work.OmitVFlag)
// Passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v.
{Name: "bench", PassToTest: true},
{Name: "benchmem", BoolVar: new(bool), PassToTest: true},
{Name: "benchtime", PassToTest: true},
{Name: "blockprofile", PassToTest: true},
{Name: "blockprofilerate", PassToTest: true},
{Name: "count", PassToTest: true},
{Name: "coverprofile", PassToTest: true},
{Name: "cpu", PassToTest: true},
{Name: "cpuprofile", PassToTest: true},
{Name: "failfast", BoolVar: new(bool), PassToTest: true},
{Name: "list", PassToTest: true},
{Name: "memprofile", PassToTest: true},
{Name: "memprofilerate", PassToTest: true},
{Name: "mutexprofile", PassToTest: true},
{Name: "mutexprofilefraction", PassToTest: true},
{Name: "outputdir", PassToTest: true},
{Name: "parallel", PassToTest: true},
{Name: "run", PassToTest: true},
{Name: "short", BoolVar: new(bool), PassToTest: true},
{Name: "timeout", PassToTest: true},
{Name: "trace", PassToTest: true},
{Name: "v", BoolVar: &testV, PassToTest: true},
cf := CmdTest.Flag
cf.BoolVar(&testC, "c", false, "")
cf.BoolVar(&cfg.BuildI, "i", false, "")
cf.StringVar(&testO, "o", "", "")
cf.BoolVar(&testCover, "cover", false, "")
cf.Var(coverFlag{(*coverModeFlag)(&testCoverMode)}, "covermode", "")
cf.Var(coverFlag{commaListFlag{&testCoverPaths}}, "coverpkg", "")
cf.Var((*base.StringsFlag)(&work.ExecCmd), "exec", "")
cf.BoolVar(&testJSON, "json", false, "")
cf.Var(&testVet, "vet", "")
// Register flags to be forwarded to the test binary. We retain variables for
// some of them so that cmd/go knows what to do with the test output, or knows
// to build the test in a way that supports the use of the flag.
cf.StringVar(&testBench, "bench", "", "")
cf.Bool("benchmem", false, "")
cf.String("benchtime", "", "")
cf.StringVar(&testBlockProfile, "blockprofile", "", "")
cf.String("blockprofilerate", "", "")
cf.Int("count", 0, "")
cf.Var(coverFlag{stringFlag{&testCoverProfile}}, "coverprofile", "")
cf.String("cpu", "", "")
cf.StringVar(&testCPUProfile, "cpuprofile", "", "")
cf.Bool("failfast", false, "")
cf.StringVar(&testList, "list", "", "")
cf.StringVar(&testMemProfile, "memprofile", "", "")
cf.String("memprofilerate", "", "")
cf.StringVar(&testMutexProfile, "mutexprofile", "", "")
cf.String("mutexprofilefraction", "", "")
cf.Var(outputdirFlag{&testOutputDir}, "outputdir", "")
cf.Int("parallel", 0, "")
cf.String("run", "", "")
cf.Bool("short", false, "")
cf.DurationVar(&testTimeout, "timeout", 10*time.Minute, "")
cf.StringVar(&testTrace, "trace", "", "")
cf.BoolVar(&testV, "v", false, "")
for name, _ := range passFlagToTest {
cf.Var(cf.Lookup(name).Value, "test."+name, "")
}
}
// add build flags to testFlagDefn
func init() {
cmdflag.AddKnownFlags("test", testFlagDefn)
var cmd base.Command
work.AddBuildFlags(&cmd, work.DefaultBuildFlags)
cmd.Flag.VisitAll(func(f *flag.Flag) {
if f.Name == "v" {
// test overrides the build -v flag
return
// A coverFlag is a flag.Value that also implies -cover.
type coverFlag struct{ v flag.Value }
func (f coverFlag) String() string { return f.v.String() }
func (f coverFlag) Set(value string) error {
if err := f.v.Set(value); err != nil {
return err
}
testCover = true
return nil
}
type coverModeFlag string
func (f *coverModeFlag) String() string { return string(*f) }
func (f *coverModeFlag) Set(value string) error {
switch value {
case "", "set", "count", "atomic":
*f = coverModeFlag(value)
return nil
default:
return errors.New(`valid modes are "set", "count", or "atomic"`)
}
}
// A commaListFlag is a flag.Value representing a comma-separated list.
type commaListFlag struct{ vals *[]string }
func (f commaListFlag) String() string { return strings.Join(*f.vals, ",") }
func (f commaListFlag) Set(value string) error {
if value == "" {
*f.vals = nil
} else {
*f.vals = strings.Split(value, ",")
}
return nil
}
// A stringFlag is a flag.Value representing a single string.
type stringFlag struct{ val *string }
func (f stringFlag) String() string { return *f.val }
func (f stringFlag) Set(value string) error {
*f.val = value
return nil
}
// outputdirFlag implements the -outputdir flag.
// It interprets an empty value as the working directory of the 'go' command.
type outputdirFlag struct {
resolved *string
}
func (f outputdirFlag) String() string { return *f.resolved }
func (f outputdirFlag) Set(value string) (err error) {
if value == "" {
// The empty string implies the working directory of the 'go' command.
*f.resolved = base.Cwd
} else {
*f.resolved, err = filepath.Abs(value)
}
return err
}
// vetFlag implements the special parsing logic for the -vet flag:
// a comma-separated list, with a distinguished value "off" and
// a boolean tracking whether it was set explicitly.
type vetFlag struct {
explicit bool
off bool
flags []string // passed to vet when invoked automatically during 'go test'
}
func (f *vetFlag) String() string {
if f.off {
return "off"
}
var buf strings.Builder
for i, f := range f.flags {
if i > 0 {
buf.WriteByte(',')
}
testFlagDefn = append(testFlagDefn, &cmdflag.Defn{
Name: f.Name,
Value: f.Value,
})
})
buf.WriteString(f)
}
return buf.String()
}
func (f *vetFlag) Set(value string) error {
if value == "" {
*f = vetFlag{flags: defaultVetFlags}
return nil
}
if value == "off" {
*f = vetFlag{
explicit: true,
off: true,
}
return nil
}
if strings.Contains(value, "=") {
return fmt.Errorf("-vet argument cannot contain equal signs")
}
if strings.Contains(value, " ") {
return fmt.Errorf("-vet argument is comma-separated list, cannot contain spaces")
}
*f = vetFlag{explicit: true}
for _, arg := range strings.Split(value, ",") {
if arg == "" {
return fmt.Errorf("-vet argument contains empty list element")
}
f.flags = append(f.flags, "-"+arg)
}
return nil
}
// testFlags processes the command line, grabbing -x and -c, rewriting known flags
// to have "test" before them, and reading the command line for the 6.out.
// to have "test" before them, and reading the command line for the test binary.
// Unfortunately for us, we need to do our own flag processing because go test
// grabs some flags but otherwise its command line is just a holding place for
// pkg.test's arguments.
@ -87,117 +203,154 @@ func init() {
// to allow both
// go test fmt -custom-flag-for-fmt-test
// go test -x math
func testFlags(usage func(), args []string) (packageNames, passToTest []string) {
goflags := cmdflag.FindGOFLAGS(testFlagDefn)
args = str.StringList(goflags, args)
inPkg := false
var explicitArgs []string
for i := 0; i < len(args); i++ {
if !strings.HasPrefix(args[i], "-") {
if !inPkg && packageNames == nil {
// First package name we've seen.
inPkg = true
}
if inPkg {
packageNames = append(packageNames, args[i])
continue
}
func testFlags(args []string) (packageNames, passToTest []string) {
base.SetFromGOFLAGS(&CmdTest.Flag)
addFromGOFLAGS := map[string]bool{}
CmdTest.Flag.Visit(func(f *flag.Flag) {
if short := strings.TrimPrefix(f.Name, "test."); passFlagToTest[short] {
addFromGOFLAGS[f.Name] = true
}
})
explicitArgs := make([]string, 0, len(args))
inPkgList := false
for len(args) > 0 {
f, remainingArgs, err := cmdflag.ParseOne(&CmdTest.Flag, args)
if errors.Is(err, flag.ErrHelp) {
exitWithUsage()
}
if inPkg {
// Found an argument beginning with "-"; end of package list.
inPkg = false
if errors.Is(err, cmdflag.ErrFlagTerminator) {
// 'go list' allows package arguments to be named either before or after
// the terminator, but 'go test' has historically allowed them only
// before. Preserve that behavior and treat all remaining arguments —
// including the terminator itself! — as arguments to the test.
explicitArgs = append(explicitArgs, args...)
break
}
f, value, extraWord := cmdflag.Parse(cmd, usage, testFlagDefn, args, i)
if f == nil {
// This is a flag we do not know; we must assume
// that any args we see after this might be flag
// arguments, not package names.
inPkg = false
if packageNames == nil {
// make non-nil: we have seen the empty package list
packageNames = []string{}
}
if args[i] == "-args" || args[i] == "--args" {
// -args or --args signals that everything that follows
// should be passed to the test.
explicitArgs = args[i+1:]
if nf := (cmdflag.NonFlagError{}); errors.As(err, &nf) {
if !inPkgList && packageNames != nil {
// We already saw the package list previously, and this argument is not
// a flag, so it — and everything after it — must be a literal argument
// to the test binary.
explicitArgs = append(explicitArgs, args...)
break
}
passToTest = append(passToTest, args[i])
inPkgList = true
packageNames = append(packageNames, nf.RawArg)
args = remainingArgs // Consume the package name.
continue
}
if i < len(goflags) {
f.Present = false // Not actually present on the command line.
if inPkgList {
// This argument is syntactically a flag, so if we were in the package
// list we're not anymore.
inPkgList = false
}
if f.Value != nil {
if err := f.Value.Set(value); err != nil {
base.Fatalf("invalid flag argument for -%s: %v", f.Name, err)
if nd := (cmdflag.FlagNotDefinedError{}); errors.As(err, &nd) {
// This is a flag we do not know. We must assume that any args we see
// after this might be flag arguments, not package names, so make
// packageNames non-nil to indicate that the package list is complete.
//
// (Actually, we only strictly need to assume that if the flag is not of
// the form -x=value, but making this more precise would be a breaking
// change in the command line API.)
if packageNames == nil {
packageNames = []string{}
}
} else {
// Test-only flags.
// Arguably should be handled by f.Value, but aren't.
switch f.Name {
// bool flags.
case "c", "i", "v", "cover", "json":
cmdflag.SetBool(cmd, f.BoolVar, value)
if f.Name == "json" && testJSON {
passToTest = append(passToTest, "-test.v=true")
}
case "o":
testO = value
testNeedBinary = true
case "exec":
xcmd, err := str.SplitQuotedFields(value)
if err != nil {
base.Fatalf("invalid flag argument for -%s: %v", f.Name, err)
}
work.ExecCmd = xcmd
case "bench":
// record that we saw the flag; don't care about the value
testBench = true
case "list":
testList = true
case "timeout":
testTimeout = value
case "blockprofile", "cpuprofile", "memprofile", "mutexprofile":
testProfile = "-" + f.Name
testNeedBinary = true
case "trace":
testProfile = "-trace"
case "coverpkg":
testCover = true
if value == "" {
testCoverPaths = nil
} else {
testCoverPaths = strings.Split(value, ",")
}
case "coverprofile":
testCover = true
testCoverProfile = value
case "covermode":
switch value {
case "set", "count", "atomic":
testCoverMode = value
default:
base.Fatalf("invalid flag argument for -covermode: %q", value)
}
testCover = true
case "outputdir":
testOutputDir = value
case "vet":
testVetList = value
if nd.RawArg == "-args" || nd.RawArg == "--args" {
// -args or --args signals that everything that follows
// should be passed to the test.
explicitArgs = append(explicitArgs, remainingArgs...)
break
}
explicitArgs = append(explicitArgs, nd.RawArg)
args = remainingArgs
continue
}
if extraWord {
i++
if err != nil {
fmt.Fprintln(os.Stderr, err)
exitWithUsage()
}
if f.PassToTest {
passToTest = append(passToTest, "-test."+f.Name+"="+value)
if short := strings.TrimPrefix(f.Name, "test."); passFlagToTest[short] {
explicitArgs = append(explicitArgs, fmt.Sprintf("-test.%s=%v", short, f.Value))
// This flag has been overridden explicitly, so don't forward its implicit
// value from GOFLAGS.
delete(addFromGOFLAGS, short)
delete(addFromGOFLAGS, "test."+short)
}
args = remainingArgs
}
var injectedFlags []string
if testJSON {
// If converting to JSON, we need the full output in order to pipe it to
// test2json.
injectedFlags = append(injectedFlags, "-test.v=true")
delete(addFromGOFLAGS, "v")
delete(addFromGOFLAGS, "test.v")
}
// Inject flags from GOFLAGS before the explicit command-line arguments.
// (They must appear before the flag terminator or first non-flag argument.)
// Also determine whether flags with awkward defaults have already been set.
var timeoutSet, outputDirSet bool
CmdTest.Flag.Visit(func(f *flag.Flag) {
short := strings.TrimPrefix(f.Name, "test.")
if addFromGOFLAGS[f.Name] {
injectedFlags = append(injectedFlags, fmt.Sprintf("-test.%s=%v", short, f.Value))
}
switch short {
case "timeout":
timeoutSet = true
case "outputdir":
outputDirSet = true
}
})
// 'go test' has a default timeout, but the test binary itself does not.
// If the timeout wasn't set (and forwarded) explicitly, add the default
// timeout to the command line.
if testTimeout > 0 && !timeoutSet {
injectedFlags = append(injectedFlags, fmt.Sprintf("-test.timeout=%v", testTimeout))
}
// Similarly, the test binary defaults -test.outputdir to its own working
// directory, but 'go test' defaults it to the working directory of the 'go'
// command. Set it explicitly if it is needed due to some other flag that
// requests output.
if testProfile() != "" && !outputDirSet {
injectedFlags = append(injectedFlags, "-test.outputdir="+testOutputDir)
}
// If the user is explicitly passing -help or -h, show output
// of the test binary so that the help output is displayed
// even though the test will exit with success.
// This loop is imperfect: it will do the wrong thing for a case
// like -args -test.outputdir -help. Such cases are probably rare,
// and getting this wrong doesn't do too much harm.
helpLoop:
for _, arg := range explicitArgs {
switch arg {
case "--":
break helpLoop
case "-h", "-help", "--help":
testHelp = true
break helpLoop
}
}
// Ensure that -race and -covermode are compatible.
if testCoverMode == "" {
testCoverMode = "set"
if cfg.BuildRace {
@ -205,35 +358,18 @@ func testFlags(usage func(), args []string) (packageNames, passToTest []string)
testCoverMode = "atomic"
}
}
testVetExplicit = testVetList != ""
if testVetList != "" && testVetList != "off" {
if strings.Contains(testVetList, "=") {
base.Fatalf("-vet argument cannot contain equal signs")
}
if strings.Contains(testVetList, " ") {
base.Fatalf("-vet argument is comma-separated list, cannot contain spaces")
}
list := strings.Split(testVetList, ",")
for i, arg := range list {
list[i] = "-" + arg
}
testVetFlags = list
}
if cfg.BuildRace && testCoverMode != "atomic" {
base.Fatalf(`-covermode must be "atomic", not %q, when -race is enabled`, testCoverMode)
}
// Tell the test what directory we're running in, so it can write the profiles there.
if testProfile != "" && testOutputDir == "" {
dir, err := os.Getwd()
if err != nil {
base.Fatalf("error from os.Getwd: %s", err)
}
passToTest = append(passToTest, "-test.outputdir", dir)
}
passToTest = append(passToTest, explicitArgs...)
return
// Forward any unparsed arguments (following --args) to the test binary.
return packageNames, append(injectedFlags, explicitArgs...)
}
func exitWithUsage() {
fmt.Fprintf(os.Stderr, "usage: %s\n", CmdTest.UsageLine)
fmt.Fprintf(os.Stderr, "Run 'go help %s' and 'go help %s' for details.\n", CmdTest.LongName(), HelpTestflag.LongName())
base.SetExitStatus(2)
base.Exit()
}

View file

@ -53,6 +53,11 @@ var (
func runVersion(cmd *base.Command, args []string) {
if len(args) == 0 {
if *versionM || *versionV {
fmt.Fprintf(os.Stderr, "go version: flags can only be used with arguments\n")
base.SetExitStatus(2)
return
}
fmt.Printf("go version %s %s/%s\n", runtime.Version(), runtime.GOOS, runtime.GOARCH)
return
}
@ -61,6 +66,7 @@ func runVersion(cmd *base.Command, args []string) {
info, err := os.Stat(arg)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
base.SetExitStatus(1)
continue
}
if info.IsDir() {

View file

@ -13,8 +13,12 @@ import (
"path/filepath"
)
// Break init loop.
func init() {
CmdVet.Run = runVet
}
var CmdVet = &base.Command{
Run: runVet,
CustomFlags: true,
UsageLine: "go vet [-n] [-x] [-vettool prog] [build flags] [vet flags] [packages]",
Short: "report likely mistakes in packages",
@ -47,7 +51,7 @@ See also: go fmt, go fix.
func runVet(cmd *base.Command, args []string) {
modload.LoadTests = true
vetFlags, pkgArgs := vetFlags(vetUsage, args)
vetFlags, pkgArgs := vetFlags(args)
work.BuildInit()
work.VetFlags = vetFlags

View file

@ -7,6 +7,7 @@ package vet
import (
"bytes"
"encoding/json"
"errors"
"flag"
"fmt"
"log"
@ -17,7 +18,6 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cmdflag"
"cmd/go/internal/str"
"cmd/go/internal/work"
)
@ -39,37 +39,44 @@ import (
var vetTool string // -vettool
func init() {
work.AddBuildFlags(CmdVet, work.DefaultBuildFlags)
CmdVet.Flag.StringVar(&vetTool, "vettool", "", "")
}
func parseVettoolFlag(args []string) {
// Extract -vettool by ad hoc flag processing:
// its value is needed even before we can declare
// the flags available during main flag processing.
for i, arg := range os.Args {
for i, arg := range args {
if arg == "-vettool" || arg == "--vettool" {
if i+1 >= len(os.Args) {
if i+1 >= len(args) {
log.Fatalf("%s requires a filename", arg)
}
vetTool = os.Args[i+1]
break
vetTool = args[i+1]
return
} else if strings.HasPrefix(arg, "-vettool=") ||
strings.HasPrefix(arg, "--vettool=") {
vetTool = arg[strings.IndexByte(arg, '=')+1:]
break
return
}
}
}
// vetFlags processes the command line, splitting it at the first non-flag
// into the list of flags and list of packages.
func vetFlags(usage func(), args []string) (passToVet, packageNames []string) {
func vetFlags(args []string) (passToVet, packageNames []string) {
parseVettoolFlag(args)
// Query the vet command for its flags.
tool := vetTool
if tool != "" {
var tool string
if vetTool == "" {
tool = base.Tool("vet")
} else {
var err error
tool, err = filepath.Abs(tool)
tool, err = filepath.Abs(vetTool)
if err != nil {
log.Fatal(err)
}
} else {
tool = base.Tool("vet")
}
out := new(bytes.Buffer)
vetcmd := exec.Command(tool, "-flags")
@ -90,95 +97,85 @@ func vetFlags(usage func(), args []string) (passToVet, packageNames []string) {
base.Exit()
}
// Add vet's flags to vetflagDefn.
// Add vet's flags to CmdVet.Flag.
//
// Some flags, in particular -tags and -v, are known to vet but
// also defined as build flags. This works fine, so we don't
// define them here but use AddBuildFlags to init them.
// also defined as build flags. This works fine, so we omit duplicates here.
// However some, like -x, are known to the build but not to vet.
var vetFlagDefn []*cmdflag.Defn
isVetFlag := make(map[string]bool, len(analysisFlags))
cf := CmdVet.Flag
for _, f := range analysisFlags {
switch f.Name {
case "tags", "v":
continue
isVetFlag[f.Name] = true
if cf.Lookup(f.Name) == nil {
if f.Bool {
cf.Bool(f.Name, false, "")
} else {
cf.String(f.Name, "", "")
}
}
defn := &cmdflag.Defn{
Name: f.Name,
PassToTest: true,
}
if f.Bool {
defn.BoolVar = new(bool)
}
vetFlagDefn = append(vetFlagDefn, defn)
}
// Add build flags to vetFlagDefn.
var cmd base.Command
work.AddBuildFlags(&cmd, work.DefaultBuildFlags)
// This flag declaration is a placeholder:
// -vettool is actually parsed by the init function above.
cmd.Flag.StringVar(new(string), "vettool", "", "path to vet tool binary")
cmd.Flag.VisitAll(func(f *flag.Flag) {
vetFlagDefn = append(vetFlagDefn, &cmdflag.Defn{
Name: f.Name,
Value: f.Value,
})
// Record the set of vet tool flags set by GOFLAGS. We want to pass them to
// the vet tool, but only if they aren't overridden by an explicit argument.
base.SetFromGOFLAGS(&CmdVet.Flag)
addFromGOFLAGS := map[string]bool{}
CmdVet.Flag.Visit(func(f *flag.Flag) {
if isVetFlag[f.Name] {
addFromGOFLAGS[f.Name] = true
}
})
// Process args.
goflags := cmdflag.FindGOFLAGS(vetFlagDefn)
args = str.StringList(goflags, args)
for i := 0; i < len(args); i++ {
if !strings.HasPrefix(args[i], "-") {
return args[:i], args[i:]
explicitFlags := make([]string, 0, len(args))
for len(args) > 0 {
f, remainingArgs, err := cmdflag.ParseOne(&CmdVet.Flag, args)
if errors.Is(err, flag.ErrHelp) {
exitWithUsage()
}
f, value, extraWord := cmdflag.Parse("vet", usage, vetFlagDefn, args, i)
if f == nil {
fmt.Fprintf(os.Stderr, "vet: flag %q not defined\n", args[i])
fmt.Fprintf(os.Stderr, "Run \"go help vet\" for more information\n")
base.SetExitStatus(2)
base.Exit()
if errors.Is(err, cmdflag.ErrFlagTerminator) {
// All remaining args must be package names, but the flag terminator is
// not included.
packageNames = remainingArgs
break
}
if i < len(goflags) {
f.Present = false // Not actually present on the command line.
if nf := (cmdflag.NonFlagError{}); errors.As(err, &nf) {
// Everything from here on out — including the argument we just consumed —
// must be a package name.
packageNames = args
break
}
if f.Value != nil {
if err := f.Value.Set(value); err != nil {
base.Fatalf("invalid flag argument for -%s: %v", f.Name, err)
}
keep := f.PassToTest
if !keep {
// A build flag, probably one we don't want to pass to vet.
// Can whitelist.
switch f.Name {
case "tags", "v":
keep = true
}
}
if !keep {
// Flags known to the build but not to vet, so must be dropped.
if extraWord {
args = append(args[:i], args[i+2:]...)
extraWord = false
} else {
args = append(args[:i], args[i+1:]...)
}
i--
}
if err != nil {
fmt.Fprintln(os.Stderr, err)
exitWithUsage()
}
if extraWord {
i++
if isVetFlag[f.Name] {
// Forward the raw arguments rather than cleaned equivalents, just in
// case the vet tool parses them idiosyncratically.
explicitFlags = append(explicitFlags, args[:len(args)-len(remainingArgs)]...)
// This flag has been overridden explicitly, so don't forward its implicit
// value from GOFLAGS.
delete(addFromGOFLAGS, f.Name)
}
args = remainingArgs
}
return args, nil
// Prepend arguments from GOFLAGS before other arguments.
CmdVet.Flag.Visit(func(f *flag.Flag) {
if addFromGOFLAGS[f.Name] {
passToVet = append(passToVet, fmt.Sprintf("-%s=%s", f.Name, f.Value))
}
})
passToVet = append(passToVet, explicitFlags...)
return passToVet, packageNames
}
var vetUsage func()
func init() { vetUsage = usage } // break initialization cycle
func usage() {
func exitWithUsage() {
fmt.Fprintf(os.Stderr, "usage: %s\n", CmdVet.UsageLine)
fmt.Fprintf(os.Stderr, "Run 'go help %s' for details.\n", CmdVet.LongName())

View file

@ -89,7 +89,7 @@ func GetBytes(u *url.URL) ([]byte, error) {
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("reading %s: %v", Redacted(u), err)
return nil, fmt.Errorf("reading %s: %v", u.Redacted(), err)
}
return b, nil
}
@ -183,21 +183,6 @@ func Get(security SecurityMode, u *url.URL) (*Response, error) {
return get(security, u)
}
// Redacted returns a redacted string form of the URL,
// suitable for printing in error messages.
// The string form replaces any non-empty password
// in the original URL with "[redacted]".
func Redacted(u *url.URL) string {
if u.User != nil {
if _, ok := u.User.Password(); ok {
redacted := *u
redacted.User = url.UserPassword(u.User.Username(), "[redacted]")
u = &redacted
}
}
return u.String()
}
// OpenBrowser attempts to open the requested URL in a web browser.
func OpenBrowser(url string) (opened bool) {
return openBrowser(url)

View file

@ -13,6 +13,7 @@ package web
import (
"crypto/tls"
"errors"
"fmt"
"mime"
"net/http"
@ -47,6 +48,13 @@ var securityPreservingHTTPClient = &http.Client{
lastHop := via[len(via)-1].URL
return fmt.Errorf("redirected from secure URL %s to insecure URL %s", lastHop, req.URL)
}
// Go's http.DefaultClient allows 10 redirects before returning an error.
// The securityPreservingHTTPClient also uses this default policy to avoid
// Go command hangs.
if len(via) >= 10 {
return errors.New("stopped after 10 redirects")
}
return nil
},
}
@ -60,14 +68,14 @@ func get(security SecurityMode, url *urlpkg.URL) (*Response, error) {
if os.Getenv("TESTGOPROXY404") == "1" && url.Host == "proxy.golang.org" {
res := &Response{
URL: Redacted(url),
URL: url.Redacted(),
Status: "404 testing",
StatusCode: 404,
Header: make(map[string][]string),
Body: http.NoBody,
}
if cfg.BuildX {
fmt.Fprintf(os.Stderr, "# get %s: %v (%.3fs)\n", Redacted(url), res.Status, time.Since(start).Seconds())
fmt.Fprintf(os.Stderr, "# get %s: %v (%.3fs)\n", url.Redacted(), res.Status, time.Since(start).Seconds())
}
return res, nil
}
@ -78,7 +86,7 @@ func get(security SecurityMode, url *urlpkg.URL) (*Response, error) {
// We print extra logging in -x mode instead, which traces what
// commands are executed.
if cfg.BuildX {
fmt.Fprintf(os.Stderr, "# get %s\n", Redacted(url))
fmt.Fprintf(os.Stderr, "# get %s\n", url.Redacted())
}
req, err := http.NewRequest("GET", url.String(), nil)
@ -111,7 +119,7 @@ func get(security SecurityMode, url *urlpkg.URL) (*Response, error) {
fetched, res, err = fetch(secure)
if err != nil {
if cfg.BuildX {
fmt.Fprintf(os.Stderr, "# get %s: %v\n", Redacted(secure), err)
fmt.Fprintf(os.Stderr, "# get %s: %v\n", secure.Redacted(), err)
}
if security != Insecure || url.Scheme == "https" {
// HTTPS failed, and we can't fall back to plain HTTP.
@ -126,9 +134,9 @@ func get(security SecurityMode, url *urlpkg.URL) (*Response, error) {
case "http":
if security == SecureOnly {
if cfg.BuildX {
fmt.Fprintf(os.Stderr, "# get %s: insecure\n", Redacted(url))
fmt.Fprintf(os.Stderr, "# get %s: insecure\n", url.Redacted())
}
return nil, fmt.Errorf("insecure URL: %s", Redacted(url))
return nil, fmt.Errorf("insecure URL: %s", url.Redacted())
}
case "":
if security != Insecure {
@ -136,9 +144,9 @@ func get(security SecurityMode, url *urlpkg.URL) (*Response, error) {
}
default:
if cfg.BuildX {
fmt.Fprintf(os.Stderr, "# get %s: unsupported\n", Redacted(url))
fmt.Fprintf(os.Stderr, "# get %s: unsupported\n", url.Redacted())
}
return nil, fmt.Errorf("unsupported scheme: %s", Redacted(url))
return nil, fmt.Errorf("unsupported scheme: %s", url.Redacted())
}
insecure := new(urlpkg.URL)
@ -146,15 +154,15 @@ func get(security SecurityMode, url *urlpkg.URL) (*Response, error) {
insecure.Scheme = "http"
if insecure.User != nil && security != Insecure {
if cfg.BuildX {
fmt.Fprintf(os.Stderr, "# get %s: insecure credentials\n", Redacted(insecure))
fmt.Fprintf(os.Stderr, "# get %s: insecure credentials\n", insecure.Redacted())
}
return nil, fmt.Errorf("refusing to pass credentials to insecure URL: %s", Redacted(insecure))
return nil, fmt.Errorf("refusing to pass credentials to insecure URL: %s", insecure.Redacted())
}
fetched, res, err = fetch(insecure)
if err != nil {
if cfg.BuildX {
fmt.Fprintf(os.Stderr, "# get %s: %v\n", Redacted(insecure), err)
fmt.Fprintf(os.Stderr, "# get %s: %v\n", insecure.Redacted(), err)
}
// HTTP failed, and we already tried HTTPS if applicable.
// Report the error from the HTTP attempt.
@ -165,11 +173,11 @@ func get(security SecurityMode, url *urlpkg.URL) (*Response, error) {
// Note: accepting a non-200 OK here, so people can serve a
// meta import in their http 404 page.
if cfg.BuildX {
fmt.Fprintf(os.Stderr, "# get %s: %v (%.3fs)\n", Redacted(fetched), res.Status, time.Since(start).Seconds())
fmt.Fprintf(os.Stderr, "# get %s: %v (%.3fs)\n", fetched.Redacted(), res.Status, time.Since(start).Seconds())
}
r := &Response{
URL: Redacted(fetched),
URL: fetched.Redacted(),
Status: res.Status,
StatusCode: res.StatusCode,
Header: map[string][]string(res.Header),
@ -201,7 +209,7 @@ func getFile(u *urlpkg.URL) (*Response, error) {
if os.IsNotExist(err) {
return &Response{
URL: Redacted(u),
URL: u.Redacted(),
Status: http.StatusText(http.StatusNotFound),
StatusCode: http.StatusNotFound,
Body: http.NoBody,
@ -211,7 +219,7 @@ func getFile(u *urlpkg.URL) (*Response, error) {
if os.IsPermission(err) {
return &Response{
URL: Redacted(u),
URL: u.Redacted(),
Status: http.StatusText(http.StatusForbidden),
StatusCode: http.StatusForbidden,
Body: http.NoBody,
@ -224,7 +232,7 @@ func getFile(u *urlpkg.URL) (*Response, error) {
}
return &Response{
URL: Redacted(u),
URL: u.Redacted(),
Status: http.StatusText(http.StatusOK),
StatusCode: http.StatusOK,
Body: f,

View file

@ -42,7 +42,7 @@ type Builder struct {
IsCmdList bool // running as part of go list; set p.Stale and additional fields below
NeedError bool // list needs p.Error
NeedExport bool // list needs p.Export
NeedCompiledGoFiles bool // list needs p.CompiledGoFIles
NeedCompiledGoFiles bool // list needs p.CompiledGoFiles
objdirSeq int // counter for NewObjdir
pkgSeq int

View file

@ -232,6 +232,7 @@ const (
DefaultBuildFlags BuildFlagMask = 0
OmitModFlag BuildFlagMask = 1 << iota
OmitModCommonFlags
OmitVFlag
)
// AddBuildFlags adds the flags common to the build, clean, get,
@ -240,7 +241,9 @@ func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) {
cmd.Flag.BoolVar(&cfg.BuildA, "a", false, "")
cmd.Flag.BoolVar(&cfg.BuildN, "n", false, "")
cmd.Flag.IntVar(&cfg.BuildP, "p", cfg.BuildP, "")
cmd.Flag.BoolVar(&cfg.BuildV, "v", false, "")
if mask&OmitVFlag == 0 {
cmd.Flag.BoolVar(&cfg.BuildV, "v", false, "")
}
cmd.Flag.BoolVar(&cfg.BuildX, "x", false, "")
cmd.Flag.Var(&load.BuildAsmflags, "asmflags", "")

View file

@ -222,7 +222,7 @@ func pkgImportPath(pkgpath string) *load.Package {
func TestRespectSetgidDir(t *testing.T) {
switch runtime.GOOS {
case "darwin":
if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" {
if runtime.GOARCH == "arm64" {
t.Skip("can't set SetGID bit with chmod on iOS")
}
case "windows", "plan9":

View file

@ -185,7 +185,7 @@ func (b *Builder) toolID(name string) string {
cmdline := str.StringList(cfg.BuildToolexec, path, "-V=full")
cmd := exec.Command(cmdline[0], cmdline[1:]...)
cmd.Env = base.EnvForDir(cmd.Dir, os.Environ())
cmd.Env = base.AppendPWD(os.Environ(), cmd.Dir)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
@ -244,7 +244,7 @@ func (b *Builder) gccgoToolID(name, language string) (string, error) {
// compile an empty file on standard input.
cmdline := str.StringList(cfg.BuildToolexec, name, "-###", "-x", language, "-c", "-")
cmd := exec.Command(cmdline[0], cmdline[1:]...)
cmd.Env = base.EnvForDir(cmd.Dir, os.Environ())
cmd.Env = base.AppendPWD(os.Environ(), cmd.Dir)
// Force untranslated output so that we see the string "version".
cmd.Env = append(cmd.Env, "LC_ALL=C")
out, err := cmd.CombinedOutput()

View file

@ -8,11 +8,6 @@ package work
import (
"bytes"
"cmd/go/internal/base"
"cmd/go/internal/cache"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str"
"encoding/json"
"errors"
"fmt"
@ -30,6 +25,12 @@ import (
"strings"
"sync"
"time"
"cmd/go/internal/base"
"cmd/go/internal/cache"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/str"
)
// actionList returns the list of actions in the dag rooted at root
@ -490,6 +491,10 @@ func (b *Builder) build(a *Action) (err error) {
return nil
}
if err := allowInstall(a); err != nil {
return err
}
// make target directory
dir, _ := filepath.Split(a.Target)
if dir != "" {
@ -1194,6 +1199,10 @@ func (b *Builder) link(a *Action) (err error) {
return err
}
if err := allowInstall(a); err != nil {
return err
}
// make target directory
dir, _ := filepath.Split(a.Target)
if dir != "" {
@ -1368,6 +1377,10 @@ func (b *Builder) getPkgConfigFlags(p *load.Package) (cflags, ldflags []string,
}
func (b *Builder) installShlibname(a *Action) error {
if err := allowInstall(a); err != nil {
return err
}
// TODO: BuildN
a1 := a.Deps[0]
err := ioutil.WriteFile(a.Target, []byte(filepath.Base(a1.Target)+"\n"), 0666)
@ -1418,6 +1431,10 @@ func (b *Builder) linkShared(a *Action) (err error) {
}
defer b.flushOutput(a)
if err := allowInstall(a); err != nil {
return err
}
if err := b.Mkdir(a.Objdir); err != nil {
return err
}
@ -1483,8 +1500,12 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) {
// advertise it by touching the mtimes (usually the libraries are up
// to date).
if !a.buggyInstall && !b.IsCmdList {
now := time.Now()
os.Chtimes(a.Target, now, now)
if cfg.BuildN {
b.Showcmd("", "touch %s", a.Target)
} else if err := allowInstall(a); err == nil {
now := time.Now()
os.Chtimes(a.Target, now, now)
}
}
return nil
}
@ -1495,6 +1516,9 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) {
a.built = a1.built
return nil
}
if err := allowInstall(a); err != nil {
return err
}
if err := b.Mkdir(a.Objdir); err != nil {
return err
@ -1524,6 +1548,13 @@ func BuildInstallFunc(b *Builder, a *Action) (err error) {
return b.moveOrCopyFile(a.Target, a1.built, perm, false)
}
// allowInstall returns a non-nil error if this invocation of the go command is
// allowed to install a.Target.
//
// (The build of cmd/go running under its own test is forbidden from installing
// to its original GOROOT.)
var allowInstall = func(*Action) error { return nil }
// cleanup removes a's object dir to keep the amount of
// on-disk garbage down in a large build. On an operating system
// with aggressive buffering, cleaning incrementally like
@ -1687,6 +1718,10 @@ func (b *Builder) installHeader(a *Action) error {
return nil
}
if err := allowInstall(a); err != nil {
return err
}
dir, _ := filepath.Split(a.Target)
if dir != "" {
if err := b.Mkdir(dir); err != nil {
@ -1927,7 +1962,7 @@ func (b *Builder) runOut(a *Action, dir string, env []string, cmdargs ...interfa
cleanup := passLongArgsInResponseFiles(cmd)
defer cleanup()
cmd.Dir = dir
cmd.Env = base.EnvForDir(cmd.Dir, os.Environ())
cmd.Env = base.AppendPWD(os.Environ(), cmd.Dir)
cmd.Env = append(cmd.Env, env...)
start := time.Now()
err := cmd.Run()
@ -2134,9 +2169,41 @@ func (b *Builder) gfortran(a *Action, p *load.Package, workdir, out string, flag
func (b *Builder) ccompile(a *Action, p *load.Package, outfile string, flags []string, file string, compiler []string) error {
file = mkAbs(p.Dir, file)
desc := p.ImportPath
if !filepath.IsAbs(outfile) {
outfile = filepath.Join(p.Dir, outfile)
outfile = mkAbs(p.Dir, outfile)
// Elide source directory paths if -trimpath or GOROOT_FINAL is set.
// This is needed for source files (e.g., a .c file in a package directory).
// TODO(golang.org/issue/36072): cgo also generates files with #line
// directives pointing to the source directory. It should not generate those
// when -trimpath is enabled.
if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") {
if cfg.BuildTrimpath {
// Keep in sync with Action.trimpath.
// The trimmed paths are a little different, but we need to trim in the
// same situations.
var from, toPath string
if m := p.Module; m != nil {
from = m.Dir
toPath = m.Path + "@" + m.Version
} else {
from = p.Dir
toPath = p.ImportPath
}
// -fdebug-prefix-map requires an absolute "to" path (or it joins the path
// with the working directory). Pick something that makes sense for the
// target platform.
var to string
if cfg.BuildContext.GOOS == "windows" {
to = filepath.Join(`\\_\_`, toPath)
} else {
to = filepath.Join("/_", toPath)
}
flags = append(flags[:len(flags):len(flags)], "-fdebug-prefix-map="+from+"="+to)
} else if p.Goroot && cfg.GOROOT_FINAL != cfg.GOROOT {
flags = append(flags[:len(flags):len(flags)], "-fdebug-prefix-map="+cfg.GOROOT+"="+cfg.GOROOT_FINAL)
}
}
output, err := b.runOut(a, filepath.Dir(file), b.cCompilerEnv(), compiler, flags, "-o", outfile, "-c", filepath.Base(file))
if len(output) > 0 {
// On FreeBSD 11, when we pass -g to clang 3.8 it
@ -2375,13 +2442,25 @@ func (b *Builder) gccSupportsFlag(compiler []string, flag string) bool {
if b.flagCache == nil {
b.flagCache = make(map[[2]string]bool)
}
tmp := os.DevNull
if runtime.GOOS == "windows" {
f, err := ioutil.TempFile(b.WorkDir, "")
if err != nil {
return false
}
f.Close()
tmp = f.Name()
defer os.Remove(tmp)
}
// We used to write an empty C file, but that gets complicated with
// go build -n. We tried using a file that does not exist, but that
// fails on systems with GCC version 4.2.1; that is the last GPLv2
// version of GCC, so some systems have frozen on it.
// Now we pass an empty file on stdin, which should work at least for
// GCC and clang.
cmdArgs := str.StringList(compiler, flag, "-c", "-x", "c", "-", "-o", os.DevNull)
cmdArgs := str.StringList(compiler, flag, "-c", "-x", "c", "-", "-o", tmp)
if cfg.BuildN || cfg.BuildX {
b.Showcmd(b.WorkDir, "%s || true", joinUnambiguously(cmdArgs))
if cfg.BuildN {
@ -2390,7 +2469,7 @@ func (b *Builder) gccSupportsFlag(compiler []string, flag string) bool {
}
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
cmd.Dir = b.WorkDir
cmd.Env = base.EnvForDir(cmd.Dir, os.Environ())
cmd.Env = base.AppendPWD(os.Environ(), cmd.Dir)
cmd.Env = append(cmd.Env, "LC_ALL=C")
out, _ := cmd.CombinedOutput()
// GCC says "unrecognized command line option".
@ -2946,13 +3025,13 @@ func mkAbsFiles(dir string, files []string) []string {
return abs
}
// passLongArgsInResponseFiles modifies cmd on Windows such that, for
// passLongArgsInResponseFiles modifies cmd such that, for
// certain programs, long arguments are passed in "response files", a
// file on disk with the arguments, with one arg per line. An actual
// argument starting with '@' means that the rest of the argument is
// a filename of arguments to expand.
//
// See Issue 18468.
// See issues 18468 (Windows) and 37768 (Darwin).
func passLongArgsInResponseFiles(cmd *exec.Cmd) (cleanup func()) {
cleanup = func() {} // no cleanup by default
@ -2990,11 +3069,6 @@ func passLongArgsInResponseFiles(cmd *exec.Cmd) (cleanup func()) {
}
func useResponseFile(path string, argLen int) bool {
// Unless we're on Windows, don't use response files.
if runtime.GOOS != "windows" {
return false
}
// Unless the program uses objabi.Flagparse, which understands
// response files, don't use response files.
// TODO: do we need more commands? asm? cgo? For now, no.
@ -3007,6 +3081,8 @@ func useResponseFile(path string, argLen int) bool {
// Windows has a limit of 32 KB arguments. To be conservative and not
// worry about whether that includes spaces or not, just use 30 KB.
// Darwin's limit is less clear. The OS claims 256KB, but we've seen
// failures with arglen as small as 50KB.
if argLen > (30 << 10) {
return true
}

View file

@ -37,6 +37,17 @@ func (gcToolchain) linker() string {
return base.Tool("link")
}
func pkgPath(a *Action) string {
p := a.Package
ppath := p.ImportPath
if cfg.BuildBuildmode == "plugin" {
ppath = pluginPath(a)
} else if p.Name == "main" && !p.Internal.ForceLibrary {
ppath = "main"
}
return ppath
}
func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, symabis string, asmhdr bool, gofiles []string) (ofile string, output []byte, err error) {
p := a.Package
objdir := a.Objdir
@ -47,12 +58,7 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, s
ofile = objdir + out
}
pkgpath := p.ImportPath
if cfg.BuildBuildmode == "plugin" {
pkgpath = pluginPath(a)
} else if p.Name == "main" && !p.Internal.ForceLibrary {
pkgpath = "main"
}
pkgpath := pkgPath(a)
gcargs := []string{"-p", pkgpath}
if p.Module != nil && p.Module.GoVersion != "" && allowedVersion(p.Module.GoVersion) {
gcargs = append(gcargs, "-lang=go"+p.Module.GoVersion)
@ -162,7 +168,7 @@ func gcBackendConcurrency(gcflags []string) int {
CheckFlags:
for _, flag := range gcflags {
// Concurrent compilation is presumed incompatible with any gcflags,
// except for a small whitelist of commonly used flags.
// except for known commonly used flags.
// If the user knows better, they can manually add their own -c to the gcflags.
switch flag {
case "-N", "-l", "-S", "-B", "-C", "-I":
@ -217,6 +223,10 @@ CheckFlags:
// trimpath returns the -trimpath argument to use
// when compiling the action.
func (a *Action) trimpath() string {
// Keep in sync with Builder.ccompile
// The trimmed paths are a little different, but we need to trim in the
// same situations.
// Strip the object directory entirely.
objdir := a.Objdir
if len(objdir) > 1 && objdir[len(objdir)-1] == filepath.Separator {
@ -240,7 +250,8 @@ func (a *Action) trimpath() string {
func asmArgs(a *Action, p *load.Package) []interface{} {
// Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files.
inc := filepath.Join(cfg.GOROOT, "pkg", "include")
args := []interface{}{cfg.BuildToolexec, base.Tool("asm"), "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags}
pkgpath := pkgPath(a)
args := []interface{}{cfg.BuildToolexec, base.Tool("asm"), "-p", pkgpath, "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags}
if p.ImportPath == "runtime" && cfg.Goarch == "386" {
for _, arg := range forcedAsmflags {
if arg == "-dynlink" {

View file

@ -10,11 +10,13 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/internal/objabi"
"cmd/internal/sys"
"flag"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
)
@ -34,6 +36,20 @@ func BuildInit() {
}
cfg.BuildPkgdir = p
}
// For each experiment that has been enabled in the toolchain, define a
// build tag with the same name but prefixed by "goexperiment." which can be
// used for compiling alternative files for the experiment. This allows
// changes for the experiment, like extra struct fields in the runtime,
// without affecting the base non-experiment code at all. [2:] strips the
// leading "X:" from objabi.Expstring().
exp := objabi.Expstring()[2:]
if exp != "none" {
experiments := strings.Split(exp, ",")
for _, expt := range experiments {
cfg.BuildContext.BuildTags = append(cfg.BuildContext.BuildTags, "goexperiment."+expt)
}
}
}
func instrumentInit() {
@ -69,7 +85,12 @@ func instrumentInit() {
modeFlag := "-" + mode
if !cfg.BuildContext.CgoEnabled {
fmt.Fprintf(os.Stderr, "go %s: %s requires cgo; enable cgo by setting CGO_ENABLED=1\n", flag.Args()[0], modeFlag)
if runtime.GOOS != cfg.Goos || runtime.GOARCH != cfg.Goarch {
fmt.Fprintf(os.Stderr, "go %s: %s requires cgo\n", flag.Args()[0], modeFlag)
} else {
fmt.Fprintf(os.Stderr, "go %s: %s requires cgo; enable cgo by setting CGO_ENABLED=1\n", flag.Args()[0], modeFlag)
}
base.SetExitStatus(2)
base.Exit()
}
@ -102,7 +123,7 @@ func buildModeInit() {
switch cfg.Goos {
case "darwin":
switch cfg.Goarch {
case "arm", "arm64":
case "arm64":
codegenArg = "-shared"
}
@ -134,9 +155,11 @@ func buildModeInit() {
case "android":
codegenArg = "-shared"
ldBuildmode = "pie"
case "windows":
ldBuildmode = "pie"
case "darwin":
switch cfg.Goarch {
case "arm", "arm64":
case "arm64":
codegenArg = "-shared"
}
fallthrough
@ -161,8 +184,12 @@ func buildModeInit() {
}
if gccgo {
codegenArg = "-fPIE"
} else if cfg.Goos != "aix" {
codegenArg = "-shared"
} else {
switch cfg.Goos {
case "aix", "windows":
default:
codegenArg = "-shared"
}
}
ldBuildmode = "pie"
case "shared":

View file

@ -184,7 +184,9 @@ var validLinkerFlags = []*lazyregexp.Regexp{
re(`-Wl,--enable-new-dtags`),
re(`-Wl,--end-group`),
re(`-Wl,--(no-)?export-dynamic`),
re(`-Wl,-E`),
re(`-Wl,-framework,[^,@\-][^,]+`),
re(`-Wl,--hash-style=(sysv|gnu|both)`),
re(`-Wl,-headerpad_max_install_names`),
re(`-Wl,--no-undefined`),
re(`-Wl,-R([^@\-][^,@]*$)`),
@ -200,6 +202,7 @@ var validLinkerFlags = []*lazyregexp.Regexp{
re(`-Wl,-undefined[=,]([^,@\-][^,]+)`),
re(`-Wl,-?-unresolved-symbols=[^,]+`),
re(`-Wl,--(no-)?warn-([^,]+)`),
re(`-Wl,-?-wrap[=,][^,@\-][^,]*`),
re(`-Wl,-z,(no)?execstack`),
re(`-Wl,-z,relro`),

View file

@ -131,6 +131,7 @@ var goodLinkerFlags = [][]string{
{"-mtune=happybirthday"},
{"-pic"},
{"-pthread"},
{"-Wl,--hash-style=both"},
{"-Wl,-rpath,foo"},
{"-Wl,-rpath,$ORIGIN/foo"},
{"-Wl,-R", "/foo"},
@ -208,6 +209,7 @@ var badLinkerFlags = [][]string{
{"-Wl,-framework", "-Wl,@Home"},
{"-Wl,-framework", "@Home"},
{"-Wl,-framework,Chocolate,@Home"},
{"-Wl,--hash-style=foo"},
{"-x", "--c"},
{"-x", "@obj"},
{"-Wl,-rpath,@foo"},

View file

@ -8,10 +8,41 @@
package work
import "os"
import (
"cmd/go/internal/cfg"
"cmd/go/internal/search"
"fmt"
"os"
"path/filepath"
"runtime"
)
func init() {
if v := os.Getenv("TESTGO_VERSION"); v != "" {
runtimeVersion = v
}
if testGOROOT := os.Getenv("TESTGO_GOROOT"); testGOROOT != "" {
// Disallow installs to the GOROOT from which testgo was built.
// Installs to other GOROOTs — such as one set explicitly within a test — are ok.
allowInstall = func(a *Action) error {
if cfg.BuildN {
return nil
}
rel := search.InDir(a.Target, testGOROOT)
if rel == "" {
return nil
}
callerPos := ""
if _, file, line, ok := runtime.Caller(1); ok {
if shortFile := search.InDir(file, filepath.Join(testGOROOT, "src")); shortFile != "" {
file = shortFile
}
callerPos = fmt.Sprintf("%s:%d: ", file, line)
}
return fmt.Errorf("%stestgo must not write to GOROOT (installing to %s)", callerPos, filepath.Join("GOROOT", rel))
}
}
}

View file

@ -59,6 +59,7 @@ func init() {
version.CmdVersion,
vet.CmdVet,
help.HelpBuildConstraint,
help.HelpBuildmode,
help.HelpC,
help.HelpCache,
@ -185,7 +186,7 @@ BigCmdLoop:
if cmd.CustomFlags {
args = args[1:]
} else {
base.SetFromGOFLAGS(cmd.Flag)
base.SetFromGOFLAGS(&cmd.Flag)
cmd.Flag.Parse(args[1:])
args = cmd.Flag.Args()
}

View file

@ -20,6 +20,8 @@ func TestNoteReading(t *testing.T) {
// both in internal and external linking mode.
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.tempFile("hello.go", `package main; func main() { print("hello, world\n") }`)
const buildID = "TestNoteReading-Build-ID"
tg.run("build", "-ldflags", "-buildid="+buildID, "-o", tg.path("hello.exe"), tg.path("hello.go"))
@ -53,7 +55,7 @@ func TestNoteReading(t *testing.T) {
// we've had trouble reading the notes generated by gold.
err := tg.doRun([]string{"build", "-ldflags", "-buildid=" + buildID + " -linkmode=external -extldflags=-fuse-ld=gold", "-o", tg.path("hello3.exe"), tg.path("hello.go")})
if err != nil {
if tg.grepCountBoth("(invalid linker|gold|cannot find 'ld')") > 0 {
if tg.grepCountBoth("(invalid linker|gold|cannot find [']ld['])") > 0 {
// It's not an error if gold isn't there. gcc claims it "cannot find 'ld'" if
// ld.gold is missing, see issue #22340.
t.Log("skipping gold test")

View file

@ -174,6 +174,25 @@ func proxyHandler(w http.ResponseWriter, r *http.Request) {
return
}
// Request for $GOPROXY/redirect/<count>/... goes to redirects.
if strings.HasPrefix(path, "redirect/") {
path = path[len("redirect/"):]
if j := strings.Index(path, "/"); j >= 0 {
count, err := strconv.Atoi(path[:j])
if err != nil {
return
}
// The last redirect.
if count <= 1 {
http.Redirect(w, r, fmt.Sprintf("/mod/%s", path[j+1:]), 302)
return
}
http.Redirect(w, r, fmt.Sprintf("/mod/redirect/%d/%s", count-1, path[j+1:]), 302)
return
}
}
// Request for $GOPROXY/sumdb/<name>/supported
// is checking whether it's OK to access sumdb via the proxy.
if path == "sumdb/"+testSumDBName+"/supported" {

View file

@ -10,6 +10,7 @@ package main_test
import (
"bytes"
"context"
"errors"
"fmt"
"go/build"
"internal/testenv"
@ -30,6 +31,7 @@ import (
"cmd/go/internal/robustio"
"cmd/go/internal/txtar"
"cmd/go/internal/work"
"cmd/internal/objabi"
"cmd/internal/sys"
)
@ -76,15 +78,26 @@ type testScript struct {
stderr string // standard error from last 'go' command; for 'stderr' command
stopped bool // test wants to stop early
start time.Time // time phase started
background []backgroundCmd // backgrounded 'exec' and 'go' commands
background []*backgroundCmd // backgrounded 'exec' and 'go' commands
}
type backgroundCmd struct {
cmd *exec.Cmd
wait <-chan struct{}
neg bool // if true, cmd should fail
want simpleStatus
args []string
cancel context.CancelFunc
done <-chan struct{}
err error
stdout, stderr strings.Builder
}
type simpleStatus string
const (
success simpleStatus = ""
failure simpleStatus = "!"
successOrFailure simpleStatus = "?"
)
var extraEnvKeys = []string{
"SYSTEMROOT", // must be preserved on Windows to find DLLs; golang.org/issue/25210
"WINDIR", // must be preserved on Windows to be able to run PowerShell command; golang.org/issue/30711
@ -109,12 +122,16 @@ func (ts *testScript) setup() {
"CCACHE_DISABLE=1", // ccache breaks with non-existent HOME
"GOARCH=" + runtime.GOARCH,
"GOCACHE=" + testGOCACHE,
"GODEBUG=" + os.Getenv("GODEBUG"),
"GOEXE=" + cfg.ExeSuffix,
"GOEXPSTRING=" + objabi.Expstring()[2:],
"GOOS=" + runtime.GOOS,
"GOPATH=" + filepath.Join(ts.workdir, "gopath"),
"GOPROXY=" + proxyURL,
"GOPRIVATE=",
"GOROOT=" + testGOROOT,
"GOROOT_FINAL=" + os.Getenv("GOROOT_FINAL"), // causes spurious rebuilds and breaks the "stale" built-in if not propagated
"TESTGO_GOROOT=" + testGOROOT,
"GOSUMDB=" + testSumDBVerifierKey,
"GONOPROXY=",
"GONOSUMDB=",
@ -181,10 +198,10 @@ func (ts *testScript) run() {
// before we print PASS. If we return early (e.g., due to a test failure),
// don't print anything about the processes that were still running.
for _, bg := range ts.background {
interruptProcess(bg.cmd.Process)
bg.cancel()
}
for _, bg := range ts.background {
<-bg.wait
<-bg.done
}
ts.background = nil
@ -205,7 +222,7 @@ func (ts *testScript) run() {
// With -v or -testwork, start log with full environment.
if *testWork || testing.Verbose() {
// Display environment.
ts.cmdEnv(false, nil)
ts.cmdEnv(success, nil)
fmt.Fprintf(&ts.log, "\n")
ts.mark = ts.log.Len()
}
@ -245,7 +262,7 @@ Script:
// Parse input line. Ignore blanks entirely.
parsed := ts.parse(line)
if parsed.name == "" {
if parsed.neg || len(parsed.conds) > 0 {
if parsed.want != "" || len(parsed.conds) > 0 {
ts.fatalf("missing command")
}
continue
@ -324,7 +341,7 @@ Script:
if cmd == nil {
ts.fatalf("unknown command %q", parsed.name)
}
cmd(ts, parsed.neg, parsed.args)
cmd(ts, parsed.want, parsed.args)
// Command can ask script to stop early.
if ts.stopped {
@ -335,9 +352,9 @@ Script:
}
for _, bg := range ts.background {
interruptProcess(bg.cmd.Process)
bg.cancel()
}
ts.cmdWait(false, nil)
ts.cmdWait(success, nil)
// Final phase ended.
rewind()
@ -352,7 +369,7 @@ Script:
//
// NOTE: If you make changes here, update testdata/script/README too!
//
var scriptCmds = map[string]func(*testScript, bool, []string){
var scriptCmds = map[string]func(*testScript, simpleStatus, []string){
"addcrlf": (*testScript).cmdAddcrlf,
"cc": (*testScript).cmdCc,
"cd": (*testScript).cmdCd,
@ -385,7 +402,7 @@ var regexpCmd = map[string]bool{
}
// addcrlf adds CRLF line endings to the named files.
func (ts *testScript) cmdAddcrlf(neg bool, args []string) {
func (ts *testScript) cmdAddcrlf(want simpleStatus, args []string) {
if len(args) == 0 {
ts.fatalf("usage: addcrlf file...")
}
@ -399,21 +416,21 @@ func (ts *testScript) cmdAddcrlf(neg bool, args []string) {
}
// cc runs the C compiler along with platform specific options.
func (ts *testScript) cmdCc(neg bool, args []string) {
func (ts *testScript) cmdCc(want simpleStatus, args []string) {
if len(args) < 1 || (len(args) == 1 && args[0] == "&") {
ts.fatalf("usage: cc args... [&]")
}
var b work.Builder
b.Init()
ts.cmdExec(neg, append(b.GccCmd(".", ""), args...))
ts.cmdExec(want, append(b.GccCmd(".", ""), args...))
robustio.RemoveAll(b.WorkDir)
}
// cd changes to a different directory.
func (ts *testScript) cmdCd(neg bool, args []string) {
if neg {
ts.fatalf("unsupported: ! cd")
func (ts *testScript) cmdCd(want simpleStatus, args []string) {
if want != success {
ts.fatalf("unsupported: %v cd", want)
}
if len(args) != 1 {
ts.fatalf("usage: cd dir")
@ -437,9 +454,9 @@ func (ts *testScript) cmdCd(neg bool, args []string) {
}
// chmod changes permissions for a file or directory.
func (ts *testScript) cmdChmod(neg bool, args []string) {
if neg {
ts.fatalf("unsupported: ! chmod")
func (ts *testScript) cmdChmod(want simpleStatus, args []string) {
if want != success {
ts.fatalf("unsupported: %v chmod", want)
}
if len(args) < 2 {
ts.fatalf("usage: chmod perm paths...")
@ -459,10 +476,10 @@ func (ts *testScript) cmdChmod(neg bool, args []string) {
}
// cmp compares two files.
func (ts *testScript) cmdCmp(neg bool, args []string) {
if neg {
func (ts *testScript) cmdCmp(want simpleStatus, args []string) {
if want != success {
// It would be strange to say "this file can have any content except this precise byte sequence".
ts.fatalf("unsupported: ! cmp")
ts.fatalf("unsupported: %v cmp", want)
}
quiet := false
if len(args) > 0 && args[0] == "-q" {
@ -476,9 +493,9 @@ func (ts *testScript) cmdCmp(neg bool, args []string) {
}
// cmpenv compares two files with environment variable substitution.
func (ts *testScript) cmdCmpenv(neg bool, args []string) {
if neg {
ts.fatalf("unsupported: ! cmpenv")
func (ts *testScript) cmdCmpenv(want simpleStatus, args []string) {
if want != success {
ts.fatalf("unsupported: %v cmpenv", want)
}
quiet := false
if len(args) > 0 && args[0] == "-q" {
@ -524,7 +541,7 @@ func (ts *testScript) doCmdCmp(args []string, env, quiet bool) {
}
// cp copies files, maybe eventually directories.
func (ts *testScript) cmdCp(neg bool, args []string) {
func (ts *testScript) cmdCp(want simpleStatus, args []string) {
if len(args) < 2 {
ts.fatalf("usage: cp src... dst")
}
@ -564,20 +581,21 @@ func (ts *testScript) cmdCp(neg bool, args []string) {
targ = filepath.Join(dst, filepath.Base(src))
}
err := ioutil.WriteFile(targ, data, mode)
if neg {
switch want {
case failure:
if err == nil {
ts.fatalf("unexpected command success")
}
} else {
case success:
ts.check(err)
}
}
}
// env displays or adds to the environment.
func (ts *testScript) cmdEnv(neg bool, args []string) {
if neg {
ts.fatalf("unsupported: ! env")
func (ts *testScript) cmdEnv(want simpleStatus, args []string) {
if want != success {
ts.fatalf("unsupported: %v env", want)
}
conv := func(s string) string { return s }
@ -615,86 +633,96 @@ func (ts *testScript) cmdEnv(neg bool, args []string) {
}
// exec runs the given command.
func (ts *testScript) cmdExec(neg bool, args []string) {
func (ts *testScript) cmdExec(want simpleStatus, args []string) {
if len(args) < 1 || (len(args) == 1 && args[0] == "&") {
ts.fatalf("usage: exec program [args...] [&]")
}
var err error
background := false
if len(args) > 0 && args[len(args)-1] == "&" {
var cmd *exec.Cmd
cmd, err = ts.execBackground(args[0], args[1:len(args)-1]...)
if err == nil {
wait := make(chan struct{})
go func() {
ctxWait(testCtx, cmd)
close(wait)
}()
ts.background = append(ts.background, backgroundCmd{cmd, wait, neg})
}
ts.stdout, ts.stderr = "", ""
} else {
ts.stdout, ts.stderr, err = ts.exec(args[0], args[1:]...)
if ts.stdout != "" {
fmt.Fprintf(&ts.log, "[stdout]\n%s", ts.stdout)
}
if ts.stderr != "" {
fmt.Fprintf(&ts.log, "[stderr]\n%s", ts.stderr)
}
if err == nil && neg {
ts.fatalf("unexpected command success")
}
background = true
args = args[:len(args)-1]
}
bg, err := ts.startBackground(want, args[0], args[1:]...)
if err != nil {
fmt.Fprintf(&ts.log, "[%v]\n", err)
if testCtx.Err() != nil {
ts.fatalf("test timed out while running command")
} else if !neg {
ts.fatalf("unexpected command failure")
}
ts.fatalf("unexpected error starting command: %v", err)
}
if background {
ts.stdout, ts.stderr = "", ""
ts.background = append(ts.background, bg)
return
}
<-bg.done
ts.stdout = bg.stdout.String()
ts.stderr = bg.stderr.String()
if ts.stdout != "" {
fmt.Fprintf(&ts.log, "[stdout]\n%s", ts.stdout)
}
if ts.stderr != "" {
fmt.Fprintf(&ts.log, "[stderr]\n%s", ts.stderr)
}
if bg.err != nil {
fmt.Fprintf(&ts.log, "[%v]\n", bg.err)
}
ts.checkCmd(bg)
}
// exists checks that the list of files exists.
func (ts *testScript) cmdExists(neg bool, args []string) {
var readonly bool
if len(args) > 0 && args[0] == "-readonly" {
readonly = true
args = args[1:]
func (ts *testScript) cmdExists(want simpleStatus, args []string) {
if want == successOrFailure {
ts.fatalf("unsupported: %v exists", want)
}
var readonly, exec bool
loop:
for len(args) > 0 {
switch args[0] {
case "-readonly":
readonly = true
args = args[1:]
case "-exec":
exec = true
args = args[1:]
default:
break loop
}
}
if len(args) == 0 {
ts.fatalf("usage: exists [-readonly] file...")
ts.fatalf("usage: exists [-readonly] [-exec] file...")
}
for _, file := range args {
file = ts.mkabs(file)
info, err := os.Stat(file)
if err == nil && neg {
if err == nil && want == failure {
what := "file"
if info.IsDir() {
what = "directory"
}
ts.fatalf("%s %s unexpectedly exists", what, file)
}
if err != nil && !neg {
if err != nil && want == success {
ts.fatalf("%s does not exist", file)
}
if err == nil && !neg && readonly && info.Mode()&0222 != 0 {
if err == nil && want == success && readonly && info.Mode()&0222 != 0 {
ts.fatalf("%s exists but is writable", file)
}
if err == nil && want == success && exec && runtime.GOOS != "windows" && info.Mode()&0111 == 0 {
ts.fatalf("%s exists but is not executable", file)
}
}
}
// go runs the go command.
func (ts *testScript) cmdGo(neg bool, args []string) {
ts.cmdExec(neg, append([]string{testGo}, args...))
func (ts *testScript) cmdGo(want simpleStatus, args []string) {
ts.cmdExec(want, append([]string{testGo}, args...))
}
// mkdir creates directories.
func (ts *testScript) cmdMkdir(neg bool, args []string) {
if neg {
ts.fatalf("unsupported: ! mkdir")
func (ts *testScript) cmdMkdir(want simpleStatus, args []string) {
if want != success {
ts.fatalf("unsupported: %v mkdir", want)
}
if len(args) < 1 {
ts.fatalf("usage: mkdir dir...")
@ -705,9 +733,9 @@ func (ts *testScript) cmdMkdir(neg bool, args []string) {
}
// rm removes files or directories.
func (ts *testScript) cmdRm(neg bool, args []string) {
if neg {
ts.fatalf("unsupported: ! rm")
func (ts *testScript) cmdRm(want simpleStatus, args []string) {
if want != success {
ts.fatalf("unsupported: %v rm", want)
}
if len(args) < 1 {
ts.fatalf("usage: rm file...")
@ -720,20 +748,20 @@ func (ts *testScript) cmdRm(neg bool, args []string) {
}
// skip marks the test skipped.
func (ts *testScript) cmdSkip(neg bool, args []string) {
func (ts *testScript) cmdSkip(want simpleStatus, args []string) {
if len(args) > 1 {
ts.fatalf("usage: skip [msg]")
}
if neg {
ts.fatalf("unsupported: ! skip")
if want != success {
ts.fatalf("unsupported: %v skip", want)
}
// Before we mark the test as skipped, shut down any background processes and
// make sure they have returned the correct status.
for _, bg := range ts.background {
interruptProcess(bg.cmd.Process)
bg.cancel()
}
ts.cmdWait(false, nil)
ts.cmdWait(success, nil)
if len(args) == 1 {
ts.t.Skip(args[0])
@ -742,15 +770,18 @@ func (ts *testScript) cmdSkip(neg bool, args []string) {
}
// stale checks that the named build targets are stale.
func (ts *testScript) cmdStale(neg bool, args []string) {
func (ts *testScript) cmdStale(want simpleStatus, args []string) {
if len(args) == 0 {
ts.fatalf("usage: stale target...")
}
tmpl := "{{if .Error}}{{.ImportPath}}: {{.Error.Err}}{else}}"
if neg {
tmpl := "{{if .Error}}{{.ImportPath}}: {{.Error.Err}}{{else}}"
switch want {
case failure:
tmpl += "{{if .Stale}}{{.ImportPath}} is unexpectedly stale{{end}}"
} else {
case success:
tmpl += "{{if not .Stale}}{{.ImportPath}} is unexpectedly NOT stale{{end}}"
default:
ts.fatalf("unsupported: %v stale", want)
}
tmpl += "{{end}}"
goArgs := append([]string{"list", "-e", "-f=" + tmpl}, args...)
@ -764,26 +795,30 @@ func (ts *testScript) cmdStale(neg bool, args []string) {
}
// stdout checks that the last go command standard output matches a regexp.
func (ts *testScript) cmdStdout(neg bool, args []string) {
scriptMatch(ts, neg, args, ts.stdout, "stdout")
func (ts *testScript) cmdStdout(want simpleStatus, args []string) {
scriptMatch(ts, want, args, ts.stdout, "stdout")
}
// stderr checks that the last go command standard output matches a regexp.
func (ts *testScript) cmdStderr(neg bool, args []string) {
scriptMatch(ts, neg, args, ts.stderr, "stderr")
func (ts *testScript) cmdStderr(want simpleStatus, args []string) {
scriptMatch(ts, want, args, ts.stderr, "stderr")
}
// grep checks that file content matches a regexp.
// Like stdout/stderr and unlike Unix grep, it accepts Go regexp syntax.
func (ts *testScript) cmdGrep(neg bool, args []string) {
scriptMatch(ts, neg, args, "", "grep")
func (ts *testScript) cmdGrep(want simpleStatus, args []string) {
scriptMatch(ts, want, args, "", "grep")
}
// scriptMatch implements both stdout and stderr.
func scriptMatch(ts *testScript, neg bool, args []string, text, name string) {
func scriptMatch(ts *testScript, want simpleStatus, args []string, text, name string) {
if want == successOrFailure {
ts.fatalf("unsupported: %v %s", want, name)
}
n := 0
if len(args) >= 1 && strings.HasPrefix(args[0], "-count=") {
if neg {
if want == failure {
ts.fatalf("cannot use -count= with negated match")
}
var err error
@ -803,12 +838,12 @@ func scriptMatch(ts *testScript, neg bool, args []string, text, name string) {
}
extraUsage := ""
want := 1
wantArgs := 1
if name == "grep" {
extraUsage = " file"
want = 2
wantArgs = 2
}
if len(args) != want {
if len(args) != wantArgs {
ts.fatalf("usage: %s [-count=N] 'pattern'%s", name, extraUsage)
}
@ -829,14 +864,16 @@ func scriptMatch(ts *testScript, neg bool, args []string, text, name string) {
// Matching against workdir would be misleading.
text = strings.ReplaceAll(text, ts.workdir, "$WORK")
if neg {
switch want {
case failure:
if re.MatchString(text) {
if isGrep && !quiet {
fmt.Fprintf(&ts.log, "[%s]\n%s\n", name, text)
}
ts.fatalf("unexpected match for %#q found in %s: %s", pattern, name, re.FindString(text))
}
} else {
case success:
if !re.MatchString(text) {
if isGrep && !quiet {
fmt.Fprintf(&ts.log, "[%s]\n%s\n", name, text)
@ -856,9 +893,9 @@ func scriptMatch(ts *testScript, neg bool, args []string, text, name string) {
}
// stop stops execution of the test (marking it passed).
func (ts *testScript) cmdStop(neg bool, args []string) {
if neg {
ts.fatalf("unsupported: ! stop")
func (ts *testScript) cmdStop(want simpleStatus, args []string) {
if want != success {
ts.fatalf("unsupported: %v stop", want)
}
if len(args) > 1 {
ts.fatalf("usage: stop [msg]")
@ -872,9 +909,9 @@ func (ts *testScript) cmdStop(neg bool, args []string) {
}
// symlink creates a symbolic link.
func (ts *testScript) cmdSymlink(neg bool, args []string) {
if neg {
ts.fatalf("unsupported: ! symlink")
func (ts *testScript) cmdSymlink(want simpleStatus, args []string) {
if want != success {
ts.fatalf("unsupported: %v symlink", want)
}
if len(args) != 3 || args[1] != "->" {
ts.fatalf("usage: symlink file -> target")
@ -885,9 +922,9 @@ func (ts *testScript) cmdSymlink(neg bool, args []string) {
}
// wait waits for background commands to exit, setting stderr and stdout to their result.
func (ts *testScript) cmdWait(neg bool, args []string) {
if neg {
ts.fatalf("unsupported: ! wait")
func (ts *testScript) cmdWait(want simpleStatus, args []string) {
if want != success {
ts.fatalf("unsupported: %v wait", want)
}
if len(args) > 0 {
ts.fatalf("usage: wait")
@ -895,34 +932,24 @@ func (ts *testScript) cmdWait(neg bool, args []string) {
var stdouts, stderrs []string
for _, bg := range ts.background {
<-bg.wait
<-bg.done
args := append([]string{filepath.Base(bg.cmd.Args[0])}, bg.cmd.Args[1:]...)
fmt.Fprintf(&ts.log, "[background] %s: %v\n", strings.Join(args, " "), bg.cmd.ProcessState)
args := append([]string{filepath.Base(bg.args[0])}, bg.args[1:]...)
fmt.Fprintf(&ts.log, "[background] %s: %v\n", strings.Join(args, " "), bg.err)
cmdStdout := bg.cmd.Stdout.(*strings.Builder).String()
cmdStdout := bg.stdout.String()
if cmdStdout != "" {
fmt.Fprintf(&ts.log, "[stdout]\n%s", cmdStdout)
stdouts = append(stdouts, cmdStdout)
}
cmdStderr := bg.cmd.Stderr.(*strings.Builder).String()
cmdStderr := bg.stderr.String()
if cmdStderr != "" {
fmt.Fprintf(&ts.log, "[stderr]\n%s", cmdStderr)
stderrs = append(stderrs, cmdStderr)
}
if bg.cmd.ProcessState.Success() {
if bg.neg {
ts.fatalf("unexpected command success")
}
} else {
if testCtx.Err() != nil {
ts.fatalf("test timed out while running command")
} else if !bg.neg {
ts.fatalf("unexpected command failure")
}
}
ts.checkCmd(bg)
}
ts.stdout = strings.Join(stdouts, "")
@ -950,58 +977,176 @@ func (ts *testScript) check(err error) {
}
}
func (ts *testScript) checkCmd(bg *backgroundCmd) {
select {
case <-bg.done:
default:
panic("checkCmd called when not done")
}
if bg.err == nil {
if bg.want == failure {
ts.fatalf("unexpected command success")
}
return
}
if errors.Is(bg.err, context.DeadlineExceeded) {
ts.fatalf("test timed out while running command")
}
if errors.Is(bg.err, context.Canceled) {
// The process was still running at the end of the test.
// The test must not depend on its exit status.
if bg.want != successOrFailure {
ts.fatalf("unexpected background command remaining at test end")
}
return
}
if bg.want == success {
ts.fatalf("unexpected command failure")
}
}
// exec runs the given command line (an actual subprocess, not simulated)
// in ts.cd with environment ts.env and then returns collected standard output and standard error.
func (ts *testScript) exec(command string, args ...string) (stdout, stderr string, err error) {
cmd := exec.Command(command, args...)
cmd.Dir = ts.cd
cmd.Env = append(ts.env, "PWD="+ts.cd)
var stdoutBuf, stderrBuf strings.Builder
cmd.Stdout = &stdoutBuf
cmd.Stderr = &stderrBuf
if err = cmd.Start(); err == nil {
err = ctxWait(testCtx, cmd)
bg, err := ts.startBackground(success, command, args...)
if err != nil {
return "", "", err
}
return stdoutBuf.String(), stderrBuf.String(), err
<-bg.done
return bg.stdout.String(), bg.stderr.String(), bg.err
}
// execBackground starts the given command line (an actual subprocess, not simulated)
// startBackground starts the given command line (an actual subprocess, not simulated)
// in ts.cd with environment ts.env.
func (ts *testScript) execBackground(command string, args ...string) (*exec.Cmd, error) {
func (ts *testScript) startBackground(want simpleStatus, command string, args ...string) (*backgroundCmd, error) {
done := make(chan struct{})
bg := &backgroundCmd{
want: want,
args: append([]string{command}, args...),
done: done,
cancel: func() {},
}
ctx := context.Background()
gracePeriod := 100 * time.Millisecond
if deadline, ok := ts.t.Deadline(); ok {
timeout := time.Until(deadline)
// If time allows, increase the termination grace period to 5% of the
// remaining time.
if gp := timeout / 20; gp > gracePeriod {
gracePeriod = gp
}
// Send the first termination signal with two grace periods remaining.
// If it still hasn't finished after the first period has elapsed,
// we'll escalate to os.Kill with a second period remaining until the
// test deadline..
timeout -= 2 * gracePeriod
if timeout <= 0 {
// The test has less than the grace period remaining. There is no point in
// even starting the command, because it will be terminated immediately.
// Save the expense of starting it in the first place.
bg.err = context.DeadlineExceeded
close(done)
return bg, nil
}
ctx, bg.cancel = context.WithTimeout(ctx, timeout)
}
cmd := exec.Command(command, args...)
cmd.Dir = ts.cd
cmd.Env = append(ts.env, "PWD="+ts.cd)
var stdoutBuf, stderrBuf strings.Builder
cmd.Stdout = &stdoutBuf
cmd.Stderr = &stderrBuf
return cmd, cmd.Start()
}
// ctxWait is like cmd.Wait, but terminates cmd with os.Interrupt if ctx becomes done.
//
// This differs from exec.CommandContext in that it prefers os.Interrupt over os.Kill.
// (See https://golang.org/issue/21135.)
func ctxWait(ctx context.Context, cmd *exec.Cmd) error {
errc := make(chan error, 1)
go func() { errc <- cmd.Wait() }()
select {
case err := <-errc:
return err
case <-ctx.Done():
interruptProcess(cmd.Process)
return <-errc
cmd.Stdout = &bg.stdout
cmd.Stderr = &bg.stderr
if err := cmd.Start(); err != nil {
bg.cancel()
return nil, err
}
go func() {
bg.err = waitOrStop(ctx, cmd, stopSignal(), gracePeriod)
close(done)
}()
return bg, nil
}
// interruptProcess sends os.Interrupt to p if supported, or os.Kill otherwise.
func interruptProcess(p *os.Process) {
if err := p.Signal(os.Interrupt); err != nil {
// stopSignal returns the appropriate signal to use to request that a process
// stop execution.
func stopSignal() os.Signal {
if runtime.GOOS == "windows" {
// Per https://golang.org/pkg/os/#Signal, “Interrupt is not implemented on
// Windows; using it with os.Process.Signal will return an error.”
// Fall back to Kill instead.
p.Kill()
return os.Kill
}
return os.Interrupt
}
// waitOrStop waits for the already-started command cmd by calling its Wait method.
//
// If cmd does not return before ctx is done, waitOrStop sends it the given interrupt signal.
// If killDelay is positive, waitOrStop waits that additional period for Wait to return before sending os.Kill.
//
// This function is copied from the one added to x/playground/internal in
// http://golang.org/cl/228438.
func waitOrStop(ctx context.Context, cmd *exec.Cmd, interrupt os.Signal, killDelay time.Duration) error {
if cmd.Process == nil {
panic("waitOrStop called with a nil cmd.Process — missing Start call?")
}
if interrupt == nil {
panic("waitOrStop requires a non-nil interrupt signal")
}
errc := make(chan error)
go func() {
select {
case errc <- nil:
return
case <-ctx.Done():
}
err := cmd.Process.Signal(interrupt)
if err == nil {
err = ctx.Err() // Report ctx.Err() as the reason we interrupted.
} else if err.Error() == "os: process already finished" {
errc <- nil
return
}
if killDelay > 0 {
timer := time.NewTimer(killDelay)
select {
// Report ctx.Err() as the reason we interrupted the process...
case errc <- ctx.Err():
timer.Stop()
return
// ...but after killDelay has elapsed, fall back to a stronger signal.
case <-timer.C:
}
// Wait still hasn't returned.
// Kill the process harder to make sure that it exits.
//
// Ignore any error: if cmd.Process has already terminated, we still
// want to send ctx.Err() (or the error from the Interrupt call)
// to properly attribute the signal that may have terminated it.
_ = cmd.Process.Kill()
}
errc <- err
}()
waitErr := cmd.Wait()
if interruptErr := <-errc; interruptErr != nil {
return interruptErr
}
return waitErr
}
// expand applies environment variable expansion to the string s.
@ -1044,7 +1189,7 @@ type condition struct {
// A command is a complete command parsed from a script.
type command struct {
neg bool // if true, expect the command to fail
want simpleStatus
conds []condition // all must be satisfied
name string // the name of the command; must be non-empty
args []string // shell-expanded arguments following name
@ -1079,11 +1224,13 @@ func (ts *testScript) parse(line string) command {
// Command prefix ! means negate the expectations about this command:
// go command should fail, match should not be found, etc.
if arg == "!" {
if cmd.neg {
ts.fatalf("duplicated '!' token")
// Prefix ? means allow either success or failure.
switch want := simpleStatus(arg); want {
case failure, successOrFailure:
if cmd.want != "" {
ts.fatalf("duplicated '!' or '?' token")
}
cmd.neg = true
cmd.want = want
return
}
@ -1234,6 +1381,8 @@ var diffTests = []struct {
}
func TestDiff(t *testing.T) {
t.Parallel()
for _, tt := range diffTests {
// Turn spaces into \n.
text1 := strings.ReplaceAll(tt.text1, " ", "\n")

View file

@ -1,23 +0,0 @@
// 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.
// Make sure that go test runs Example_Z before Example_A, preserving source order.
package p
import "fmt"
var n int
func Example_Z() {
n++
fmt.Println(n)
// Output: 1
}
func Example_A() {
n++
fmt.Println(n)
// Output: 2
}

View file

@ -1,21 +0,0 @@
// 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.
// Make sure that go test runs Example_Y before Example_B, preserving source order.
package p
import "fmt"
func Example_Y() {
n++
fmt.Println(n)
// Output: 3
}
func Example_B() {
n++
fmt.Println(n)
// Output: 4
}

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