libgo: update to go1.15rc1
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/245157
This commit is contained in:
parent
75a23e5903
commit
f75af8c146
916 changed files with 45830 additions and 14928 deletions
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -1 +1 @@
|
|||
go1.14.6
|
||||
go1.15rc1
|
||||
|
|
|
@ -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
6
libgo/configure
vendored
|
@ -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
|
||||
|
|
|
@ -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(, ..)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
2
libgo/go/cmd/go/internal/cache/cache.go
vendored
2
libgo/go/cmd/go/internal/cache/cache.go
vendored
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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, " "))
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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.
|
||||
`,
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = ""
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:], "/")
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ func testMain(m *testing.M) int {
|
|||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
codehost.WorkRoot = dir
|
||||
cfg.GOMODCACHE = dir
|
||||
return m.Run()
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ""
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 == "" {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
164
libgo/go/cmd/go/internal/modload/modfile.go
Normal file
164
libgo/go/cmd/go/internal/modload/modfile.go
Normal 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
|
||||
}
|
259
libgo/go/cmd/go/internal/modload/mvs.go
Normal file
259
libgo/go/cmd/go/internal/modload/mvs.go
Normal 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
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
217
libgo/go/cmd/go/internal/modload/vendor.go
Normal file
217
libgo/go/cmd/go/internal/modload/vendor.go
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
34
libgo/go/cmd/go/internal/test/flagdefs.go
Normal file
34
libgo/go/cmd/go/internal/test/flagdefs.go
Normal 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,
|
||||
}
|
34
libgo/go/cmd/go/internal/test/flagdefs_test.go
Normal file
34
libgo/go/cmd/go/internal/test/flagdefs_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
90
libgo/go/cmd/go/internal/test/genflags.go
Normal file
90
libgo/go/cmd/go/internal/test/genflags.go
Normal 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 }}
|
||||
}
|
||||
`
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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", "")
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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" {
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -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`),
|
||||
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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" {
|
||||
|
|
|
@ -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")
|
||||
|
|
23
libgo/go/cmd/go/testdata/example1_test.go
vendored
23
libgo/go/cmd/go/testdata/example1_test.go
vendored
|
@ -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
|
||||
}
|
21
libgo/go/cmd/go/testdata/example2_test.go
vendored
21
libgo/go/cmd/go/testdata/example2_test.go
vendored
|
@ -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
Loading…
Add table
Reference in a new issue