libgo: update to Go1.12beta2

Reviewed-on: https://go-review.googlesource.com/c/158019

gotools/:
	* Makefile.am (go_cmd_vet_files): Update for Go1.12beta2 release.
	(GOTOOLS_TEST_TIMEOUT): Increase to 600.
	(check-runtime): Export LD_LIBRARY_PATH before computing GOARCH
	and GOOS.
	(check-vet): Copy golang.org/x/tools into check-vet-dir.
	* Makefile.in: Regenerate.

gcc/testsuite/:
	* go.go-torture/execute/names-1.go: Stop using debug/xcoff, which
	is no longer externally visible.

From-SVN: r268084
This commit is contained in:
Ian Lance Taylor 2019-01-18 19:04:36 +00:00 committed by Ian Lance Taylor
parent 225220d668
commit 4f4a855d82
1331 changed files with 71774 additions and 45132 deletions

View File

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

View File

@ -1,3 +1,8 @@
2019-01-18 Ian Lance Taylor <iant@golang.org>
* go.go-torture/execute/names-1.go: Stop using debug/xcoff, which
is no longer externally visible.
2019-01-18 Marek Polacek <polacek@redhat.com>
PR c++/86926

View File

@ -7,9 +7,9 @@ import (
"debug/elf"
"debug/macho"
"debug/pe"
"debug/xcoff"
"fmt"
"os"
"runtime"
"strings"
)
@ -61,6 +61,12 @@ func Function3(out *bytes.Buffer) {
}
func main() {
if runtime.GOOS == "aix" {
// Not supported on AIX until there is an externally
// visible version of internal/xcoff.
return
}
var b bytes.Buffer
Function1(&b)
Function2(&b)
@ -95,10 +101,6 @@ func checkFile(f *os.File) {
for _, psym := range pf.Symbols {
syms = append(syms, psym.Name)
}
} else if xf, err := xcoff.NewFile(f); err == nil {
for _, xsym := range xf.Symbols {
syms = append(syms, xsym.Name)
}
} else {
fmt.Println("checksyms: could not parse executable")
fmt.Println("UNSUPPORTED: checksyms")

View File

@ -1,3 +1,12 @@
2019-01-18 Ian Lance Taylor <iant@golang.org>
* Makefile.am (go_cmd_vet_files): Update for Go1.12beta2 release.
(GOTOOLS_TEST_TIMEOUT): Increase to 600.
(check-runtime): Export LD_LIBRARY_PATH before computing GOARCH
and GOOS.
(check-vet): Copy golang.org/x/tools into check-vet-dir.
* Makefile.in: Regenerate.
2018-10-31 Joseph Myers <joseph@codesourcery.com>
PR bootstrap/82856

View File

@ -70,31 +70,8 @@ go_cmd_cgo_files = \
$(cmdsrcdir)/cgo/util.go
go_cmd_vet_files = \
$(cmdsrcdir)/vet/asmdecl.go \
$(cmdsrcdir)/vet/assign.go \
$(cmdsrcdir)/vet/atomic.go \
$(cmdsrcdir)/vet/bool.go \
$(cmdsrcdir)/vet/buildtag.go \
$(cmdsrcdir)/vet/cgo.go \
$(cmdsrcdir)/vet/composite.go \
$(cmdsrcdir)/vet/copylock.go \
$(cmdsrcdir)/vet/deadcode.go \
$(cmdsrcdir)/vet/dead.go \
$(cmdsrcdir)/vet/doc.go \
$(cmdsrcdir)/vet/httpresponse.go \
$(cmdsrcdir)/vet/lostcancel.go \
$(cmdsrcdir)/vet/main.go \
$(cmdsrcdir)/vet/method.go \
$(cmdsrcdir)/vet/nilfunc.go \
$(cmdsrcdir)/vet/print.go \
$(cmdsrcdir)/vet/rangeloop.go \
$(cmdsrcdir)/vet/shadow.go \
$(cmdsrcdir)/vet/shift.go \
$(cmdsrcdir)/vet/structtag.go \
$(cmdsrcdir)/vet/tests.go \
$(cmdsrcdir)/vet/types.go \
$(cmdsrcdir)/vet/unsafeptr.go \
$(cmdsrcdir)/vet/unused.go
$(cmdsrcdir)/vet/main.go
go_cmd_buildid_files = \
$(cmdsrcdir)/buildid/buildid.go \
@ -163,7 +140,7 @@ uninstall-local:
GOTESTFLAGS =
# Number of seconds before tests time out.
GOTOOLS_TEST_TIMEOUT = 480
GOTOOLS_TEST_TIMEOUT = 600
# Run tests using the go tool, and frob the output to look like that
# generated by DejaGNU. The main output of this is two files:
@ -256,6 +233,7 @@ check-runtime: go$(EXEEXT) $(noinst_PROGRAMS) check-head check-gccgo check-gcc
$(MKDIR_P) check-runtime-dir
@abs_libgodir=`cd $(libgodir) && $(PWD_COMMAND)`; \
LD_LIBRARY_PATH=`echo $${abs_libgodir}/.libs:$${LD_LIBRARY_PATH} | sed 's,::*,:,g;s,^:*,,;s,:*$$,,'`; \
export LD_LIBRARY_PATH; \
GOARCH=`$(abs_builddir)/go$(EXEEXT) env GOARCH`; \
GOOS=`$(abs_builddir)/go$(EXEEXT) env GOOS`; \
files=`$(SHELL) $(libgosrcdir)/../match.sh --goarch=$${GOARCH} --goos=$${GOOS} --srcdir=$(libgosrcdir)/runtime --extrafiles="$(libgodir)/runtime_sysinfo.go $(libgodir)/sigtab.go" --tag=libffi`; \
@ -299,10 +277,11 @@ check-carchive-test: go$(EXEEXT) $(noinst_PROGRAMS) check-head check-gccgo check
# check-vet runs `go test cmd/vet` in our environment.
check-vet: go$(EXEEXT) $(noinst_PROGRAMS) check-head check-gccgo check-gcc
rm -rf check-vet-dir cmd_vet-testlog
$(MKDIR_P) check-vet-dir/src/cmd/internal
$(MKDIR_P) check-vet-dir/src/cmd/internal check-vet-dir/src/cmd/vendor/golang.org/x
cp -r $(cmdsrcdir)/vet check-vet-dir/src/cmd/
cp -r $(cmdsrcdir)/internal/objabi check-vet-dir/src/cmd/internal
cp $(libgodir)/objabi.go check-vet-dir/src/cmd/internal/objabi/
cp -r $(libgosrcdir)/golang.org/x/tools check-vet-dir/src/cmd/vendor/golang.org/x/
@abs_libgodir=`cd $(libgodir) && $(PWD_COMMAND)`; \
abs_checkdir=`cd check-vet-dir && $(PWD_COMMAND)`; \
echo "cd check-vet-dir/src/cmd/vet && $(ECHO_ENV) GOPATH=$${abs_checkdir} $(abs_builddir)/go$(EXEEXT) test -test.short -test.timeout=$(GOTOOLS_TEST_TIMEOUT)s -test.v" > cmd_vet-testlog

View File

@ -373,31 +373,8 @@ go_cmd_cgo_files = \
$(cmdsrcdir)/cgo/util.go
go_cmd_vet_files = \
$(cmdsrcdir)/vet/asmdecl.go \
$(cmdsrcdir)/vet/assign.go \
$(cmdsrcdir)/vet/atomic.go \
$(cmdsrcdir)/vet/bool.go \
$(cmdsrcdir)/vet/buildtag.go \
$(cmdsrcdir)/vet/cgo.go \
$(cmdsrcdir)/vet/composite.go \
$(cmdsrcdir)/vet/copylock.go \
$(cmdsrcdir)/vet/deadcode.go \
$(cmdsrcdir)/vet/dead.go \
$(cmdsrcdir)/vet/doc.go \
$(cmdsrcdir)/vet/httpresponse.go \
$(cmdsrcdir)/vet/lostcancel.go \
$(cmdsrcdir)/vet/main.go \
$(cmdsrcdir)/vet/method.go \
$(cmdsrcdir)/vet/nilfunc.go \
$(cmdsrcdir)/vet/print.go \
$(cmdsrcdir)/vet/rangeloop.go \
$(cmdsrcdir)/vet/shadow.go \
$(cmdsrcdir)/vet/shift.go \
$(cmdsrcdir)/vet/structtag.go \
$(cmdsrcdir)/vet/tests.go \
$(cmdsrcdir)/vet/types.go \
$(cmdsrcdir)/vet/unsafeptr.go \
$(cmdsrcdir)/vet/unused.go
$(cmdsrcdir)/vet/main.go
go_cmd_buildid_files = \
$(cmdsrcdir)/buildid/buildid.go \
@ -726,8 +703,8 @@ distclean-generic:
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
@NATIVE_FALSE@install-exec-local:
@NATIVE_FALSE@uninstall-local:
@NATIVE_FALSE@install-exec-local:
clean: clean-am
clean-am: clean-binPROGRAMS clean-generic clean-noinstPROGRAMS \
@ -927,6 +904,7 @@ mostlyclean-local:
@NATIVE_TRUE@ $(MKDIR_P) check-runtime-dir
@NATIVE_TRUE@ @abs_libgodir=`cd $(libgodir) && $(PWD_COMMAND)`; \
@NATIVE_TRUE@ LD_LIBRARY_PATH=`echo $${abs_libgodir}/.libs:$${LD_LIBRARY_PATH} | sed 's,::*,:,g;s,^:*,,;s,:*$$,,'`; \
@NATIVE_TRUE@ export LD_LIBRARY_PATH; \
@NATIVE_TRUE@ GOARCH=`$(abs_builddir)/go$(EXEEXT) env GOARCH`; \
@NATIVE_TRUE@ GOOS=`$(abs_builddir)/go$(EXEEXT) env GOOS`; \
@NATIVE_TRUE@ files=`$(SHELL) $(libgosrcdir)/../match.sh --goarch=$${GOARCH} --goos=$${GOOS} --srcdir=$(libgosrcdir)/runtime --extrafiles="$(libgodir)/runtime_sysinfo.go $(libgodir)/sigtab.go" --tag=libffi`; \
@ -970,10 +948,11 @@ mostlyclean-local:
# check-vet runs `go test cmd/vet` in our environment.
@NATIVE_TRUE@check-vet: go$(EXEEXT) $(noinst_PROGRAMS) check-head check-gccgo check-gcc
@NATIVE_TRUE@ rm -rf check-vet-dir cmd_vet-testlog
@NATIVE_TRUE@ $(MKDIR_P) check-vet-dir/src/cmd/internal
@NATIVE_TRUE@ $(MKDIR_P) check-vet-dir/src/cmd/internal check-vet-dir/src/cmd/vendor/golang.org/x
@NATIVE_TRUE@ cp -r $(cmdsrcdir)/vet check-vet-dir/src/cmd/
@NATIVE_TRUE@ cp -r $(cmdsrcdir)/internal/objabi check-vet-dir/src/cmd/internal
@NATIVE_TRUE@ cp $(libgodir)/objabi.go check-vet-dir/src/cmd/internal/objabi/
@NATIVE_TRUE@ cp -r $(libgosrcdir)/golang.org/x/tools check-vet-dir/src/cmd/vendor/golang.org/x/
@NATIVE_TRUE@ @abs_libgodir=`cd $(libgodir) && $(PWD_COMMAND)`; \
@NATIVE_TRUE@ abs_checkdir=`cd check-vet-dir && $(PWD_COMMAND)`; \
@NATIVE_TRUE@ echo "cd check-vet-dir/src/cmd/vet && $(ECHO_ENV) GOPATH=$${abs_checkdir} $(abs_builddir)/go$(EXEEXT) test -test.short -test.timeout=$(GOTOOLS_TEST_TIMEOUT)s -test.v" > cmd_vet-testlog

View File

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

View File

@ -217,8 +217,7 @@ toolexeclibgodebug_DATA = \
debug/gosym.gox \
debug/macho.gox \
debug/pe.gox \
debug/plan9obj.gox \
debug/xcoff.gox
debug/plan9obj.gox
toolexeclibgoencodingdir = $(toolexeclibgodir)/encoding
@ -394,8 +393,8 @@ toolexeclibgounicode_DATA = \
# internal packages nothing will explicitly depend on them.
# Force them to be built.
noinst_DATA = \
golang_org/x/net/internal/nettest.gox \
golang_org/x/net/nettest.gox \
internal/x/net/internal/nettest.gox \
internal/x/net/nettest.gox \
internal/testenv.gox \
internal/trace.gox \
net/internal/socktest.gox \
@ -536,7 +535,7 @@ cpugen.go: s-cpu; @true
s-cpu: Makefile
rm -f cpugen.go.tmp
echo "package cpu" > cpugen.go.tmp
echo "const CacheLineSize = `$(SHELL) $(srcdir)/goarch.sh $(GOARCH) cachelinesize`" >> cpugen.go.tmp
echo "const CacheLinePadSize = `$(SHELL) $(srcdir)/goarch.sh $(GOARCH) cachelinesize`" >> cpugen.go.tmp
$(SHELL) $(srcdir)/mvifdiff.sh cpugen.go.tmp cpugen.go
$(STAMP) $@
@ -554,7 +553,7 @@ s-objabi: Makefile
echo 'const defaultGOARCH = runtime.GOARCH' >> objabi.go.tmp
echo 'const defaultGO_EXTLINK_ENABLED = ``' >> objabi.go.tmp
echo 'const version = `'`cat $(srcdir)/VERSION | sed 1q`' '`$(GOC) --version | sed 1q`'`' >> objabi.go.tmp
echo 'const stackGuardMultiplier = 1' >> objabi.go.tmp
echo 'const stackGuardMultiplierDefault = 1' >> objabi.go.tmp
echo 'const goexperiment = ``' >> objabi.go.tmp
$(SHELL) $(srcdir)/mvifdiff.sh objabi.go.tmp objabi.go
$(STAMP) $@
@ -715,16 +714,14 @@ PACKAGES = $(shell cat $(srcdir)/libgo-packages.txt)
libgo_go_objs = \
$(addsuffix .lo,$(PACKAGES)) \
bytes/index.lo \
internal/bytealg/bytealg.lo \
reflect/makefunc_ffi_c.lo \
strings/index.lo \
$(syscall_lib_clone_lo) \
syscall/errno.lo \
syscall/signame.lo \
syscall/wait.lo \
$(golang_org_x_net_lif_lo) \
$(golang_org_x_net_route_lo) \
$(internal_x_net_lif_lo) \
$(internal_x_net_route_lo) \
log/syslog/syslog_c.lo \
$(os_lib_inotify_lo) \
runtime/internal/atomic_c.lo \
@ -807,7 +804,7 @@ BUILDDEPS = \
BUILDPACKAGE = \
$(MKDIR_P) $(@D); \
files=`echo $^ | sed -e 's/[^ ]*\.gox//g' -e 's/[^ ]*\.dep//'`; \
$(LTGOCOMPILE) -I . -c -fgo-pkgpath=`echo $@ | sed -e 's/.lo$$//' -e 's|golang_org|vendor/golang_org|'` $($(subst -,_,$(subst .,_,$(subst /,_,$@)))_GOCFLAGS) -o $@ $$files
$(LTGOCOMPILE) -I . -c -fgo-pkgpath=`echo $@ | sed -e 's/.lo$$//'` $($(subst -,_,$(subst .,_,$(subst /,_,$@)))_GOCFLAGS) -o $@ $$files
# How to build a .gox file from a .lo file.
# Matching .o file can either be in the same directory as the .lo (non-PIC
@ -990,6 +987,7 @@ extra_check_libs_cmd_go_internal_cache = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_generate = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_get = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_load = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_lockedfile = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_imports = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_modconv = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_modfetch = $(abs_builddir)/libgotool.a
@ -1007,13 +1005,7 @@ extra_check_libs_cmd_vet_internal_cfg = $(abs_builddir)/libgotool.a
# FIXME: The following C files may as well move to the runtime
# directory and be treated like other C files.
# Use C code to speed up {bytes,strings}.IndexByte and friends.
bytes/index.lo: go/bytes/indexbyte.c runtime.inc
@$(MKDIR_P) bytes
$(LTCOMPILE) -c -o $@ $(srcdir)/go/bytes/indexbyte.c
strings/index.lo: go/strings/indexbyte.c runtime.inc
@$(MKDIR_P) strings
$(LTCOMPILE) -c -o $@ $(srcdir)/go/strings/indexbyte.c
# Use C code to speed up internal/bytealg.IndexByte and friends.
internal/bytealg/bytealg.lo: go/internal/bytealg/bytealg.c runtime.inc
@$(MKDIR_P) internal/bytealg
$(LTCOMPILE) -c -o $@ $(srcdir)/go/internal/bytealg/bytealg.c
@ -1070,34 +1062,34 @@ endif
if LIBGO_IS_BSD
# Build golang_org/x/net/route only on BSD systems.
# Build internal/x/net/route only on BSD systems.
$(eval $(call PACKAGE_template,golang_org/x/net/route))
$(eval $(call PACKAGE_template,internal/x/net/route))
golang_org_x_net_route_lo = \
golang_org/x/net/route.lo
golang_org_x_net_route_check = \
golang_org/x/net/route/check
internal_x_net_route_lo = \
internal/x/net/route.lo
internal_x_net_route_check = \
internal/x/net/route/check
endif
if LIBGO_IS_SOLARIS
# Build golang_org/x/net/lif only on Solaris systems.
# Build internal/x/net/lif only on Solaris systems.
$(eval $(call PACKAGE_template,golang_org/x/net/lif))
$(eval $(call PACKAGE_template,internal/x/net/lif))
golang_org_x_net_lif_lo = \
golang_org/x/net/lif.lo
golang_org_x_net_lif_check = \
golang_org/x/net/lif/check
internal_x_net_lif_lo = \
internal/x/net/lif.lo
internal_x_net_lif_check = \
internal_org/x/net/lif/check
endif
TPACKAGES = $(shell cat $(srcdir)/check-packages.txt)
TEST_PACKAGES = $(addsuffix /check,$(TPACKAGES)) \
$(golang_org_x_net_lif_check) \
$(golang_org_x_net_route_check)
$(internal_x_net_lif_check) \
$(internal_x_net_route_check)
check: check-tail
check-recursive: check-head

View File

@ -215,11 +215,11 @@ 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
am__DEPENDENCIES_2 = $(addsuffix .lo,$(PACKAGES)) bytes/index.lo \
am__DEPENDENCIES_2 = $(addsuffix .lo,$(PACKAGES)) \
internal/bytealg/bytealg.lo reflect/makefunc_ffi_c.lo \
strings/index.lo $(am__DEPENDENCIES_1) syscall/errno.lo \
syscall/signame.lo syscall/wait.lo $(golang_org_x_net_lif_lo) \
$(golang_org_x_net_route_lo) log/syslog/syslog_c.lo \
$(am__DEPENDENCIES_1) syscall/errno.lo syscall/signame.lo \
syscall/wait.lo $(internal_x_net_lif_lo) \
$(internal_x_net_route_lo) log/syslog/syslog_c.lo \
runtime/internal/atomic_c.lo sync/atomic_c.lo \
internal/cpu/cpu_gccgo.lo
am__DEPENDENCIES_3 =
@ -696,8 +696,7 @@ toolexeclibgodebug_DATA = \
debug/gosym.gox \
debug/macho.gox \
debug/pe.gox \
debug/plan9obj.gox \
debug/xcoff.gox
debug/plan9obj.gox
toolexeclibgoencodingdir = $(toolexeclibgodir)/encoding
toolexeclibgoencoding_DATA = \
@ -849,8 +848,8 @@ toolexeclibgounicode_DATA = \
# 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/internal/nettest.gox \
golang_org/x/net/nettest.gox internal/testenv.gox \
noinst_DATA = internal/x/net/internal/nettest.gox \
internal/x/net/nettest.gox internal/testenv.gox \
internal/trace.gox net/internal/socktest.gox \
os/signal/internal/pty.gox runtime/pprof/internal/profile.gox \
zdefaultcc.go
@ -917,16 +916,14 @@ SYSINFO_FLAGS = \
PACKAGES = $(shell cat $(srcdir)/libgo-packages.txt)
libgo_go_objs = \
$(addsuffix .lo,$(PACKAGES)) \
bytes/index.lo \
internal/bytealg/bytealg.lo \
reflect/makefunc_ffi_c.lo \
strings/index.lo \
$(syscall_lib_clone_lo) \
syscall/errno.lo \
syscall/signame.lo \
syscall/wait.lo \
$(golang_org_x_net_lif_lo) \
$(golang_org_x_net_route_lo) \
$(internal_x_net_lif_lo) \
$(internal_x_net_route_lo) \
log/syslog/syslog_c.lo \
$(os_lib_inotify_lo) \
runtime/internal/atomic_c.lo \
@ -991,7 +988,7 @@ BUILDDEPS = \
BUILDPACKAGE = \
$(MKDIR_P) $(@D); \
files=`echo $^ | sed -e 's/[^ ]*\.gox//g' -e 's/[^ ]*\.dep//'`; \
$(LTGOCOMPILE) -I . -c -fgo-pkgpath=`echo $@ | sed -e 's/.lo$$//' -e 's|golang_org|vendor/golang_org|'` $($(subst -,_,$(subst .,_,$(subst /,_,$@)))_GOCFLAGS) -o $@ $$files
$(LTGOCOMPILE) -I . -c -fgo-pkgpath=`echo $@ | sed -e 's/.lo$$//'` $($(subst -,_,$(subst .,_,$(subst /,_,$@)))_GOCFLAGS) -o $@ $$files
# How to build a .gox file from a .lo file.
@ -1105,6 +1102,7 @@ extra_check_libs_cmd_go_internal_cache = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_generate = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_get = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_load = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_lockedfile = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_imports = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_modconv = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_modfetch = $(abs_builddir)/libgotool.a
@ -1123,22 +1121,22 @@ extra_check_libs_cmd_vet_internal_cfg = $(abs_builddir)/libgotool.a
# Use a build tag, based on a configure check, to cope.
@HAVE_STAT_TIMESPEC_TRUE@@LIBGO_IS_SOLARIS_TRUE@matchargs_os = --tag=solaristag
@LIBGO_IS_SOLARIS_FALSE@matchargs_os =
@LIBGO_IS_BSD_TRUE@golang_org_x_net_route_lo = \
@LIBGO_IS_BSD_TRUE@ golang_org/x/net/route.lo
@LIBGO_IS_BSD_TRUE@internal_x_net_route_lo = \
@LIBGO_IS_BSD_TRUE@ internal/x/net/route.lo
@LIBGO_IS_BSD_TRUE@golang_org_x_net_route_check = \
@LIBGO_IS_BSD_TRUE@ golang_org/x/net/route/check
@LIBGO_IS_BSD_TRUE@internal_x_net_route_check = \
@LIBGO_IS_BSD_TRUE@ internal/x/net/route/check
@LIBGO_IS_SOLARIS_TRUE@golang_org_x_net_lif_lo = \
@LIBGO_IS_SOLARIS_TRUE@ golang_org/x/net/lif.lo
@LIBGO_IS_SOLARIS_TRUE@internal_x_net_lif_lo = \
@LIBGO_IS_SOLARIS_TRUE@ internal/x/net/lif.lo
@LIBGO_IS_SOLARIS_TRUE@golang_org_x_net_lif_check = \
@LIBGO_IS_SOLARIS_TRUE@ golang_org/x/net/lif/check
@LIBGO_IS_SOLARIS_TRUE@internal_x_net_lif_check = \
@LIBGO_IS_SOLARIS_TRUE@ internal_org/x/net/lif/check
TPACKAGES = $(shell cat $(srcdir)/check-packages.txt)
TEST_PACKAGES = $(addsuffix /check,$(TPACKAGES)) \
$(golang_org_x_net_lif_check) \
$(golang_org_x_net_route_check)
$(internal_x_net_lif_check) \
$(internal_x_net_route_check)
MOSTLYCLEANFILES = \
s-runtime_sysinfo s-sigtab s-runtime-inc s-zstdpkglist \
@ -2635,7 +2633,7 @@ cpugen.go: s-cpu; @true
s-cpu: Makefile
rm -f cpugen.go.tmp
echo "package cpu" > cpugen.go.tmp
echo "const CacheLineSize = `$(SHELL) $(srcdir)/goarch.sh $(GOARCH) cachelinesize`" >> cpugen.go.tmp
echo "const CacheLinePadSize = `$(SHELL) $(srcdir)/goarch.sh $(GOARCH) cachelinesize`" >> cpugen.go.tmp
$(SHELL) $(srcdir)/mvifdiff.sh cpugen.go.tmp cpugen.go
$(STAMP) $@
@ -2653,7 +2651,7 @@ s-objabi: Makefile
echo 'const defaultGOARCH = runtime.GOARCH' >> objabi.go.tmp
echo 'const defaultGO_EXTLINK_ENABLED = ``' >> objabi.go.tmp
echo 'const version = `'`cat $(srcdir)/VERSION | sed 1q`' '`$(GOC) --version | sed 1q`'`' >> objabi.go.tmp
echo 'const stackGuardMultiplier = 1' >> objabi.go.tmp
echo 'const stackGuardMultiplierDefault = 1' >> objabi.go.tmp
echo 'const goexperiment = ``' >> objabi.go.tmp
$(SHELL) $(srcdir)/mvifdiff.sh objabi.go.tmp objabi.go
$(STAMP) $@
@ -2846,13 +2844,7 @@ cmd/go/internal/cfg.lo.dep: $(extra_go_files_cmd_go_internal_cfg)
# FIXME: The following C files may as well move to the runtime
# directory and be treated like other C files.
# Use C code to speed up {bytes,strings}.IndexByte and friends.
bytes/index.lo: go/bytes/indexbyte.c runtime.inc
@$(MKDIR_P) bytes
$(LTCOMPILE) -c -o $@ $(srcdir)/go/bytes/indexbyte.c
strings/index.lo: go/strings/indexbyte.c runtime.inc
@$(MKDIR_P) strings
$(LTCOMPILE) -c -o $@ $(srcdir)/go/strings/indexbyte.c
# Use C code to speed up internal/bytealg.IndexByte and friends.
internal/bytealg/bytealg.lo: go/internal/bytealg/bytealg.c runtime.inc
@$(MKDIR_P) internal/bytealg
$(LTCOMPILE) -c -o $@ $(srcdir)/go/internal/bytealg/bytealg.c
@ -2895,13 +2887,13 @@ internal/cpu/cpu_gccgo.lo: go/internal/cpu/cpu_gccgo.c runtime.inc
@$(MKDIR_P) internal/cpu
$(LTCOMPILE) -c -o $@ $(srcdir)/go/internal/cpu/cpu_gccgo.c
# Build golang_org/x/net/route only on BSD systems.
# Build internal/x/net/route only on BSD systems.
@LIBGO_IS_BSD_TRUE@$(eval $(call PACKAGE_template,golang_org/x/net/route))
@LIBGO_IS_BSD_TRUE@$(eval $(call PACKAGE_template,internal/x/net/route))
# Build golang_org/x/net/lif only on Solaris systems.
# Build internal/x/net/lif only on Solaris systems.
@LIBGO_IS_SOLARIS_TRUE@$(eval $(call PACKAGE_template,golang_org/x/net/lif))
@LIBGO_IS_SOLARIS_TRUE@$(eval $(call PACKAGE_template,internal/x/net/lif))
check: check-tail
check-recursive: check-head

View File

@ -1 +1 @@
go1.11.1
go1.12beta2

View File

@ -8,6 +8,8 @@ cmd/go/internal/generate
cmd/go/internal/get
cmd/go/internal/imports
cmd/go/internal/load
cmd/go/internal/lockedfile
cmd/go/internal/lockedfile/internal/filelock
cmd/go/internal/modconv
cmd/go/internal/modfetch
cmd/go/internal/modfetch/codehost
@ -25,7 +27,6 @@ cmd/internal/buildid
cmd/internal/edit
cmd/internal/objabi
cmd/internal/test2json
cmd/vet/internal/cfg
compress/bzip2
compress/flate
compress/gzip
@ -61,7 +62,6 @@ debug/elf
debug/macho
debug/pe
debug/plan9obj
debug/xcoff
encoding/ascii85
encoding/asn1
encoding/base32
@ -91,19 +91,6 @@ go/printer
go/scanner
go/token
go/types
golang_org/x/crypto/internal/chacha20
golang_org/x/crypto/chacha20poly1305
golang_org/x/crypto/chacha20poly1305/internal/chacha20
golang_org/x/crypto/cryptobyte
golang_org/x/crypto/curve25519
golang_org/x/crypto/poly1305
golang_org/x/net/dns/dnsmessage
golang_org/x/net/http/httpguts
golang_org/x/net/http/httpproxy
golang_org/x/net/http2/hpack
golang_org/x/net/idna
golang_org/x/net/lex/httplex
golang_org/x/net/proxy
hash
hash/adler32
hash/crc32
@ -118,9 +105,22 @@ image/jpeg
image/png
index/suffixarray
internal/cpu
internal/fmtsort
internal/poll
internal/singleflight
internal/trace
internal/x/crypto/chacha20poly1305
internal/x/crypto/cryptobyte
internal/x/crypto/curve25519
internal/x/crypto/hkdf
internal/x/crypto/internal/chacha20
internal/x/crypto/poly1305
internal/x/net/dns/dnsmessage
internal/x/net/http/httpguts
internal/x/net/http/httpproxy
internal/x/net/http2/hpack
internal/x/net/idna
internal/xcoff
io
io/ioutil
log
@ -162,6 +162,7 @@ regexp/syntax
runtime
runtime/debug
runtime/internal/atomic
runtime/internal/math
runtime/internal/sys
runtime/pprof
runtime/pprof/internal/profile

4
libgo/configure vendored
View File

@ -2532,7 +2532,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
ac_config_headers="$ac_config_headers config.h"
libtool_VERSION=13:0:0
libtool_VERSION=14:0:0
# Default to --enable-multilib
@ -13744,7 +13744,7 @@ go_include="-include"
# All known GOOS values. This is the union of all operating systems
# supported by the gofrontend and all operating systems supported by
# the gc toolchain.
ALLGOOS="aix android darwin dragonfly freebsd irix linux netbsd openbsd plan9 rtems solaris windows"
ALLGOOS="aix android darwin dragonfly freebsd hurd irix js linux netbsd openbsd plan9 rtems solaris windows"
is_darwin=no
is_freebsd=no

View File

@ -10,7 +10,7 @@ AC_INIT(package-unused, version-unused,, libgo)
AC_CONFIG_SRCDIR(Makefile.am)
AC_CONFIG_HEADER(config.h)
libtool_VERSION=13:0:0
libtool_VERSION=14:0:0
AC_SUBST(libtool_VERSION)
AM_ENABLE_MULTILIB(, ..)
@ -153,7 +153,7 @@ AC_SUBST(go_include)
# All known GOOS values. This is the union of all operating systems
# supported by the gofrontend and all operating systems supported by
# the gc toolchain.
ALLGOOS="aix android darwin dragonfly freebsd irix linux netbsd openbsd plan9 rtems solaris windows"
ALLGOOS="aix android darwin dragonfly freebsd hurd irix js linux netbsd openbsd plan9 rtems solaris windows"
is_darwin=no
is_freebsd=no

View File

@ -160,7 +160,7 @@ func (b *block) V7() *headerV7 { return (*headerV7)(b) }
func (b *block) GNU() *headerGNU { return (*headerGNU)(b) }
func (b *block) STAR() *headerSTAR { return (*headerSTAR)(b) }
func (b *block) USTAR() *headerUSTAR { return (*headerUSTAR)(b) }
func (b *block) Sparse() sparseArray { return (sparseArray)(b[:]) }
func (b *block) Sparse() sparseArray { return sparseArray(b[:]) }
// GetFormat checks that the block is a valid tar header based on the checksum.
// It then attempts to guess the specific format based on magic values.
@ -263,7 +263,7 @@ func (h *headerGNU) DevMajor() []byte { return h[329:][:8] }
func (h *headerGNU) DevMinor() []byte { return h[337:][:8] }
func (h *headerGNU) AccessTime() []byte { return h[345:][:12] }
func (h *headerGNU) ChangeTime() []byte { return h[357:][:12] }
func (h *headerGNU) Sparse() sparseArray { return (sparseArray)(h[386:][:24*4+1]) }
func (h *headerGNU) Sparse() sparseArray { return sparseArray(h[386:][:24*4+1]) }
func (h *headerGNU) RealSize() []byte { return h[483:][:12] }
type headerSTAR [blockSize]byte
@ -293,7 +293,7 @@ func (h *headerUSTAR) Prefix() []byte { return h[345:][:155] }
type sparseArray []byte
func (s sparseArray) Entry(i int) sparseElem { return (sparseElem)(s[i*24:]) }
func (s sparseArray) Entry(i int) sparseElem { return sparseElem(s[i*24:]) }
func (s sparseArray) IsExtended() []byte { return s[24*s.MaxEntries():][:1] }
func (s sparseArray) MaxEntries() int { return len(s) / 24 }

View File

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

View File

@ -69,6 +69,9 @@ func OpenReader(name string) (*ReadCloser, error) {
// NewReader returns a new Reader reading from r, which is assumed to
// have the given size in bytes.
func NewReader(r io.ReaderAt, size int64) (*Reader, error) {
if size < 0 {
return nil, errors.New("zip: size cannot be negative")
}
zr := new(Reader)
if err := zr.init(r, size); err != nil {
return nil, err

View File

@ -658,6 +658,12 @@ func TestInvalidFiles(t *testing.T) {
if err != ErrFormat {
t.Errorf("sigs: error=%v, want %v", err, ErrFormat)
}
// negative size
_, err = NewReader(bytes.NewReader([]byte("foobar")), -1)
if err == nil {
t.Errorf("archive/zip.NewReader: expected error when negative size is passed")
}
}
func messWith(fileName string, corrupter func(b []byte)) (r io.ReaderAt, size int64) {

View File

@ -303,8 +303,8 @@ func (h *FileHeader) SetMode(mode os.FileMode) {
}
// isZip64 reports whether the file size exceeds the 32 bit limit
func (fh *FileHeader) isZip64() bool {
return fh.CompressedSize64 >= uint32max || fh.UncompressedSize64 >= uint32max
func (h *FileHeader) isZip64() bool {
return h.CompressedSize64 >= uint32max || h.UncompressedSize64 >= uint32max
}
func msdosModeToFileMode(m uint32) (mode os.FileMode) {

View File

@ -178,7 +178,7 @@ func (w *Writer) Close() error {
return err
}
// store max values in the regular end record to signal that
// store max values in the regular end record to signal
// that the zip64 values should be used instead
records = uint16max
size = uint32max

View File

@ -159,7 +159,7 @@ func (r *rleBuffer) Write(p []byte) (n int, err error) {
return len(p), nil
}
func min(x, y int) int {
func min(x, y int64) int64 {
if x < y {
return x
}
@ -190,7 +190,7 @@ func (r *rleBuffer) ReadAt(p []byte, off int64) (n int, err error) {
if len(parts) > 0 {
skipBytes := off - parts[0].off
for _, part := range parts {
repeat := min(int(part.n-skipBytes), len(p)-n)
repeat := int(min(part.n-skipBytes, int64(len(p)-n)))
memset(p[n:n+repeat], part.b)
n += repeat
if n == len(p) {

View File

@ -33,8 +33,8 @@ type Reader struct {
rd io.Reader // reader provided by the client
r, w int // buf read and write positions
err error
lastByte int
lastRuneSize int
lastByte int // last byte read for UnreadByte; -1 means invalid
lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid
}
const minReadBufferSize = 16
@ -63,7 +63,7 @@ func NewReader(rd io.Reader) *Reader {
}
// Size returns the size of the underlying buffer in bytes.
func (r *Reader) Size() int { return len(r.buf) }
func (b *Reader) Size() int { return len(b.buf) }
// Reset discards any buffered data, resets all state, and switches
// the buffered reader to read from r.
@ -123,11 +123,17 @@ func (b *Reader) readErr() error {
// being valid at the next read call. If Peek returns fewer than n bytes, it
// also returns an error explaining why the read is short. The error is
// ErrBufferFull if n is larger than b's buffer size.
//
// Calling Peek prevents a UnreadByte or UnreadRune call from succeeding
// until the next read operation.
func (b *Reader) Peek(n int) ([]byte, error) {
if n < 0 {
return nil, ErrNegativeCount
}
b.lastByte = -1
b.lastRuneSize = -1
for b.w-b.r < n && b.w-b.r < len(b.buf) && b.err == nil {
b.fill() // b.w-b.r < len(b.buf) => buffer is not full
}
@ -186,6 +192,7 @@ func (b *Reader) Discard(n int) (discarded int, err error) {
// It returns the number of bytes read into p.
// The bytes are taken from at most one Read on the underlying Reader,
// hence n may be less than len(p).
// To read exactly len(p) bytes, use io.ReadFull(b, p).
// At EOF, the count will be zero and err will be io.EOF.
func (b *Reader) Read(p []byte) (n int, err error) {
n = len(p)
@ -248,6 +255,10 @@ func (b *Reader) ReadByte() (byte, error) {
}
// UnreadByte unreads the last byte. Only the most recently read byte can be unread.
//
// UnreadByte returns an error if the most recent method called on the
// Reader was not a read operation. Notably, Peek is not considered a
// read operation.
func (b *Reader) UnreadByte() error {
if b.lastByte < 0 || b.r == 0 && b.w > 0 {
return ErrInvalidUnreadByte
@ -286,8 +297,8 @@ func (b *Reader) ReadRune() (r rune, size int, err error) {
return r, size, nil
}
// UnreadRune unreads the last rune. If the most recent read operation on
// the buffer was not a ReadRune, UnreadRune returns an error. (In this
// UnreadRune unreads the last rune. If the most recent method called on
// the Reader was not a ReadRune, UnreadRune returns an error. (In this
// regard it is stricter than UnreadByte, which will unread the last byte
// from any read operation.)
func (b *Reader) UnreadRune() error {
@ -314,9 +325,11 @@ func (b *Reader) Buffered() int { return b.w - b.r }
// ReadBytes or ReadString instead.
// ReadSlice returns err != nil if and only if line does not end in delim.
func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
s := 0 // search start index
for {
// Search buffer.
if i := bytes.IndexByte(b.buf[b.r:b.w], delim); i >= 0 {
if i := bytes.IndexByte(b.buf[b.r+s:b.w], delim); i >= 0 {
i += s
line = b.buf[b.r : b.r+i+1]
b.r += i + 1
break
@ -338,6 +351,8 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
break
}
s = b.w - b.r // do not rescan area we scanned before
b.fill() // buffer is not full
}

View File

@ -285,6 +285,24 @@ func TestUnreadRune(t *testing.T) {
}
}
func TestNoUnreadRuneAfterPeek(t *testing.T) {
br := NewReader(strings.NewReader("example"))
br.ReadRune()
br.Peek(1)
if err := br.UnreadRune(); err == nil {
t.Error("UnreadRune didn't fail after Peek")
}
}
func TestNoUnreadByteAfterPeek(t *testing.T) {
br := NewReader(strings.NewReader("example"))
br.ReadByte()
br.Peek(1)
if err := br.UnreadByte(); err == nil {
t.Error("UnreadByte didn't fail after Peek")
}
}
func TestUnreadByte(t *testing.T) {
segments := []string{"Hello, ", "world"}
r := NewReader(&StringReader{data: segments})
@ -550,7 +568,7 @@ func TestWriter(t *testing.T) {
t.Errorf("%s: %d bytes written", context, len(written))
}
for l := 0; l < len(written); l++ {
if written[i] != data[i] {
if written[l] != data[l] {
t.Errorf("wrong bytes written")
t.Errorf("want=%q", data[0:len(written)])
t.Errorf("have=%q", written)

View File

@ -152,6 +152,9 @@ func delete(m map[Type]Type1, key Type)
// String: the number of bytes in v.
// Channel: the number of elements queued (unread) in the channel buffer;
// if v is nil, len(v) is zero.
// For some arguments, such as a string literal or a simple array expression, the
// result can be a constant. See the Go language specification's "Length and
// capacity" section for details.
func len(v Type) int
// The cap built-in function returns the capacity of v, according to its type:
@ -161,6 +164,9 @@ func len(v Type) int
// if v is nil, cap(v) is zero.
// Channel: the channel buffer capacity, in units of elements;
// if v is nil, cap(v) is zero.
// For some arguments, such as a simple array expression, the result can be a
// constant. See the Go language specification's "Length and capacity" section for
// details.
func cap(v Type) int
// The make built-in function allocates and initializes an object of type

View File

@ -12,13 +12,15 @@ import (
"unicode/utf8"
)
// smallBufferSize is an initial allocation minimal capacity.
const smallBufferSize = 64
// A Buffer is a variable-sized buffer of bytes with Read and Write methods.
// The zero value for Buffer is an empty buffer ready to use.
type Buffer struct {
buf []byte // contents are the bytes buf[off : len(buf)]
off int // read at &buf[off], write at &buf[len(buf)]
bootstrap [64]byte // memory to hold first slice; helps small buffers avoid allocation.
lastRead readOp // last read operation, so that Unread* can work correctly.
buf []byte // contents are the bytes buf[off : len(buf)]
off int // read at &buf[off], write at &buf[len(buf)]
lastRead readOp // last read operation, so that Unread* can work correctly.
// FIXME: it would be advisable to align Buffer to cachelines to avoid false
// sharing.
@ -66,7 +68,7 @@ func (b *Buffer) String() string {
return string(b.buf[b.off:])
}
// empty returns whether the unread portion of the buffer is empty.
// empty reports whether the unread portion of the buffer is empty.
func (b *Buffer) empty() bool { return len(b.buf) <= b.off }
// Len returns the number of bytes of the unread portion of the buffer;
@ -125,9 +127,8 @@ func (b *Buffer) grow(n int) int {
if i, ok := b.tryGrowByReslice(n); ok {
return i
}
// Check if we can make use of bootstrap array.
if b.buf == nil && n <= len(b.bootstrap) {
b.buf = b.bootstrap[:n]
if b.buf == nil && n <= smallBufferSize {
b.buf = make([]byte, n, smallBufferSize)
return 0
}
c := cap(b.buf)
@ -441,9 +442,9 @@ func (b *Buffer) ReadString(delim byte) (line string, err error) {
// NewBuffer creates and initializes a new Buffer using buf as its
// initial contents. The new Buffer takes ownership of buf, and the
// caller should not use buf after this call. NewBuffer is intended to
// prepare a Buffer to read existing data. It can also be used to size
// the internal buffer for writing. To do that, buf should have the
// desired capacity but a length of zero.
// prepare a Buffer to read existing data. It can also be used to set
// the initial size of the internal buffer for writing. To do that,
// buf should have the desired capacity but a length of zero.
//
// In most cases, new(Buffer) (or just declaring a Buffer variable) is
// sufficient to initialize a Buffer.

View File

@ -293,7 +293,7 @@ func TestReadFromPanicReader(t *testing.T) {
}
check(t, "TestReadFromPanicReader (1)", &buf, "")
// Confirm that when Reader panics, the emtpy buffer remains empty
// Confirm that when Reader panics, the empty buffer remains empty
var buf2 Buffer
defer func() {
recover()

View File

@ -12,6 +12,13 @@ import (
"unicode/utf8"
)
// Equal returns a boolean reporting whether a and b
// are the same length and contain the same bytes.
// A nil argument is equivalent to an empty slice.
func Equal(a, b []byte) bool {
return bytealg.Equal(a, b)
}
func equalPortable(a, b []byte) bool {
if len(a) != len(b) {
return false
@ -24,6 +31,13 @@ func equalPortable(a, b []byte) bool {
return true
}
// Compare returns an integer comparing two byte slices lexicographically.
// The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
// A nil argument is equivalent to an empty slice.
func Compare(a, b []byte) int {
return bytealg.Compare(a, b)
}
// explode splits s into a slice of UTF-8 sequences, one per Unicode code point (still slices of bytes),
// up to a maximum of n byte slices. Invalid UTF-8 sequences are chopped into individual bytes.
func explode(s []byte, n int) [][]byte {
@ -83,6 +97,11 @@ func ContainsRune(b []byte, r rune) bool {
return IndexRune(b, r) >= 0
}
// IndexByte returns the index of the first instance of c in b, or -1 if c is not present in b.
func IndexByte(b []byte, c byte) int {
return bytealg.IndexByte(b, c)
}
func indexBytePortable(s []byte, c byte) int {
for i, b := range s {
if b == c {
@ -489,19 +508,19 @@ func ToTitle(s []byte) []byte { return Map(unicode.ToTitle, s) }
// ToUpperSpecial treats s as UTF-8-encoded bytes and returns a copy with all the Unicode letters mapped to their
// upper case, giving priority to the special casing rules.
func ToUpperSpecial(c unicode.SpecialCase, s []byte) []byte {
return Map(func(r rune) rune { return c.ToUpper(r) }, s)
return Map(c.ToUpper, s)
}
// ToLowerSpecial treats s as UTF-8-encoded bytes and returns a copy with all the Unicode letters mapped to their
// lower case, giving priority to the special casing rules.
func ToLowerSpecial(c unicode.SpecialCase, s []byte) []byte {
return Map(func(r rune) rune { return c.ToLower(r) }, s)
return Map(c.ToLower, s)
}
// ToTitleSpecial treats s as UTF-8-encoded bytes and returns a copy with all the Unicode letters mapped to their
// title case, giving priority to the special casing rules.
func ToTitleSpecial(c unicode.SpecialCase, s []byte) []byte {
return Map(func(r rune) rune { return c.ToTitle(r) }, s)
return Map(c.ToTitle, s)
}
// isSeparator reports whether the rune could mark a word boundary.
@ -774,6 +793,15 @@ func Replace(s, old, new []byte, n int) []byte {
return t[0:w]
}
// ReplaceAll returns a copy of the slice s with all
// non-overlapping instances of old replaced by new.
// If old is empty, it matches at the beginning of the slice
// and after each UTF-8 sequence, yielding up to k+1 replacements
// for a k-rune slice.
func ReplaceAll(s, old, new []byte) []byte {
return Replace(s, old, new, -1)
}
// EqualFold reports whether s and t, interpreted as UTF-8 strings,
// are equal under Unicode case-folding.
func EqualFold(s, t []byte) bool {
@ -849,21 +877,22 @@ func Index(s, sep []byte) int {
if len(s) <= bytealg.MaxBruteForce {
return bytealg.Index(s, sep)
}
c := sep[0]
c0 := sep[0]
c1 := sep[1]
i := 0
t := s[:len(s)-n+1]
t := len(s) - n + 1
fails := 0
for i < len(t) {
if t[i] != c {
for i < t {
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(t[i:], c)
o := IndexByte(s[i:t], c0)
if o < 0 {
return -1
}
i += o
}
if Equal(s[i:i+n], sep) {
if s[i+1] == c1 && Equal(s[i:i+n], sep) {
return i
}
fails++
@ -879,24 +908,25 @@ func Index(s, sep []byte) int {
}
return -1
}
c := sep[0]
c0 := sep[0]
c1 := sep[1]
i := 0
fails := 0
t := s[:len(s)-n+1]
for i < len(t) {
if t[i] != c {
o := IndexByte(t[i:], c)
t := len(s) - n + 1
for i < t {
if s[i] != c0 {
o := IndexByte(s[i:t], c0)
if o < 0 {
break
}
i += o
}
if Equal(s[i:i+n], sep) {
if s[i+1] == c1 && Equal(s[i:i+n], sep) {
return i
}
i++
fails++
if fails >= 4+i>>4 && i < len(t) {
if fails >= 4+i>>4 && i < t {
// Give up on IndexByte, it isn't skipping ahead
// far enough to be better than Rabin-Karp.
// Experiments (using IndexPeriodic) suggest

View File

@ -1,24 +0,0 @@
// Copyright 2010 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 bytes
//go:noescape
// IndexByte returns the index of the first instance of c in b, or -1 if c is not present in b.
func IndexByte(b []byte, c byte) int // in internal/bytealg
//go:noescape
// Equal returns a boolean reporting whether a and b
// are the same length and contain the same bytes.
// A nil argument is equivalent to an empty slice.
func Equal(a, b []byte) bool // in internal/bytealg
//go:noescape
// Compare returns an integer comparing two byte slices lexicographically.
// The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
// A nil argument is equivalent to an empty slice.
func Compare(a, b []byte) int // in internal/bytealg

View File

@ -1367,6 +1367,12 @@ func TestReplace(t *testing.T) {
if cap(in) == cap(out) && &in[:1][0] == &out[:1][0] {
t.Errorf("Replace(%q, %q, %q, %d) didn't copy", tt.in, tt.old, tt.new, tt.n)
}
if tt.n == -1 {
out := ReplaceAll(in, []byte(tt.old), []byte(tt.new))
if s := string(out); s != tt.out {
t.Errorf("ReplaceAll(%q, %q, %q) = %q, want %q", tt.in, tt.old, tt.new, s, tt.out)
}
}
}
}

View File

@ -41,9 +41,16 @@ var compareTests = []struct {
func TestCompare(t *testing.T) {
for _, tt := range compareTests {
cmp := Compare(tt.a, tt.b)
if cmp != tt.i {
t.Errorf(`Compare(%q, %q) = %v`, tt.a, tt.b, cmp)
numShifts := 16
buffer := make([]byte, len(tt.b)+numShifts)
// vary the input alignment of tt.b
for offset := 0; offset <= numShifts; offset++ {
shiftedB := buffer[offset : len(tt.b)+offset]
copy(shiftedB, tt.b)
cmp := Compare(tt.a, shiftedB)
if cmp != tt.i {
t.Errorf(`Compare(%q, %q), offset %d = %v; want %v`, tt.a, tt.b, offset, cmp, tt.i)
}
}
}
}

View File

@ -39,6 +39,14 @@ func ExampleBuffer_Grow() {
// Output: "64 bytes or fewer"
}
func ExampleBuffer_Len() {
var b bytes.Buffer
b.Grow(64)
b.Write([]byte("abcde"))
fmt.Printf("%d", b.Len())
// Output: 5
}
func ExampleCompare() {
// Interpret Compare's result by comparing it to zero.
var a, b []byte
@ -290,6 +298,12 @@ func ExampleReplace() {
// moo moo moo
}
func ExampleReplaceAll() {
fmt.Printf("%s\n", bytes.ReplaceAll([]byte("oink oink oink"), []byte("oink"), []byte("moo")))
// Output:
// moo moo moo
}
func ExampleRunes() {
rs := bytes.Runes([]byte("go gopher"))
for _, r := range rs {

View File

@ -1,73 +0,0 @@
/* indexbyte.c -- implement bytes.IndexByte for Go.
Copyright 2009 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. */
#include <stddef.h>
#include "runtime.h"
#include "array.h"
/* This is in C so that the compiler can optimize it appropriately.
We deliberately don't split the stack in case it does call the
library function, which shouldn't need much stack space. */
intgo IndexByte (struct __go_open_array, char)
__asm__ (GOSYM_PREFIX "bytes.IndexByte")
__attribute__ ((no_split_stack));
intgo
IndexByte (struct __go_open_array s, char b)
{
char *p;
p = __builtin_memchr (s.__values, b, s.__count);
if (p == NULL)
return -1;
return p - (char *) s.__values;
}
/* Comparison. */
_Bool Equal (struct __go_open_array a, struct __go_open_array b)
__asm__ (GOSYM_PREFIX "bytes.Equal")
__attribute__ ((no_split_stack));
_Bool
Equal (struct __go_open_array a, struct __go_open_array b)
{
if (a.__count != b.__count)
return 0;
return __builtin_memcmp (a.__values, b.__values, a.__count) == 0;
}
intgo Compare (struct __go_open_array a, struct __go_open_array b)
__asm__ (GOSYM_PREFIX "bytes.Compare")
__attribute__ ((no_split_stack));
intgo
Compare (struct __go_open_array a, struct __go_open_array b)
{
intgo len;
len = a.__count;
if (len > b.__count)
len = b.__count;
if (len > 0)
{
intgo ret;
ret = __builtin_memcmp (a.__values, b.__values, len);
if (ret < 0)
return -1;
else if (ret > 0)
return 1;
}
if (a.__count < b.__count)
return -1;
else if (a.__count > b.__count)
return 1;
else
return 0;
}

View File

@ -14,6 +14,7 @@ import (
// io.ByteScanner, and io.RuneScanner interfaces by reading from
// a byte slice.
// Unlike a Buffer, a Reader is read-only and supports seeking.
// The zero value for Reader operates like a Reader of an empty slice.
type Reader struct {
s []byte
i int64 // current reading index
@ -75,10 +76,10 @@ func (r *Reader) ReadByte() (byte, error) {
// UnreadByte complements ReadByte in implementing the io.ByteScanner interface.
func (r *Reader) UnreadByte() error {
r.prevRune = -1
if r.i <= 0 {
return errors.New("bytes.Reader.UnreadByte: at beginning of slice")
}
r.prevRune = -1
r.i--
return nil
}
@ -101,6 +102,9 @@ func (r *Reader) ReadRune() (ch rune, size int, err error) {
// UnreadRune complements ReadRune in implementing the io.RuneScanner interface.
func (r *Reader) UnreadRune() error {
if r.i <= 0 {
return errors.New("bytes.Reader.UnreadRune: at beginning of slice")
}
if r.prevRune < 0 {
return errors.New("bytes.Reader.UnreadRune: previous operation was not ReadRune")
}

View File

@ -276,3 +276,45 @@ func TestReaderReset(t *testing.T) {
t.Errorf("ReadAll: got %q, want %q", got, want)
}
}
func TestReaderZero(t *testing.T) {
if l := (&Reader{}).Len(); l != 0 {
t.Errorf("Len: got %d, want 0", l)
}
if n, err := (&Reader{}).Read(nil); n != 0 || err != io.EOF {
t.Errorf("Read: got %d, %v; want 0, io.EOF", n, err)
}
if n, err := (&Reader{}).ReadAt(nil, 11); n != 0 || err != io.EOF {
t.Errorf("ReadAt: got %d, %v; want 0, io.EOF", n, err)
}
if b, err := (&Reader{}).ReadByte(); b != 0 || err != io.EOF {
t.Errorf("ReadByte: got %d, %v; want 0, io.EOF", b, err)
}
if ch, size, err := (&Reader{}).ReadRune(); ch != 0 || size != 0 || err != io.EOF {
t.Errorf("ReadRune: got %d, %d, %v; want 0, 0, io.EOF", ch, size, err)
}
if offset, err := (&Reader{}).Seek(11, io.SeekStart); offset != 11 || err != nil {
t.Errorf("Seek: got %d, %v; want 11, nil", offset, err)
}
if s := (&Reader{}).Size(); s != 0 {
t.Errorf("Size: got %d, want 0", s)
}
if (&Reader{}).UnreadByte() == nil {
t.Errorf("UnreadByte: got nil, want error")
}
if (&Reader{}).UnreadRune() == nil {
t.Errorf("UnreadRune: got nil, want error")
}
if n, err := (&Reader{}).WriteTo(ioutil.Discard); n != 0 || err != nil {
t.Errorf("WriteTo: got %d, %v; want 0, nil", n, err)
}
}

View File

@ -145,6 +145,7 @@ func (f *File) ParseGo(name string, src []byte) {
if f.Ref == nil {
f.Ref = make([]*Ref, 0, 8)
}
f.walk(ast2, ctxProg, (*File).validateIdents)
f.walk(ast2, ctxProg, (*File).saveExprs)
// Accumulate exported functions.
@ -181,6 +182,14 @@ func commentText(g *ast.CommentGroup) string {
return strings.Join(pieces, "")
}
func (f *File) validateIdents(x interface{}, context astContext) {
if x, ok := x.(*ast.Ident); ok {
if f.isMangledName(x.Name) {
error_(x.Pos(), "identifier %q may conflict with identifiers generated by cgo", x.Name)
}
}
}
// Save various references we are going to need later.
func (f *File) saveExprs(x interface{}, context astContext) {
switch x := x.(type) {
@ -191,6 +200,18 @@ func (f *File) saveExprs(x interface{}, context astContext) {
}
case *ast.CallExpr:
f.saveCall(x, context)
case *ast.GenDecl:
if x.Tok == token.CONST {
for _, spec := range x.Specs {
vs := spec.(*ast.ValueSpec)
if vs.Type == nil {
for _, name := range spec.(*ast.ValueSpec).Names {
consts[name.Name] = true
}
}
}
}
}
}

View File

@ -413,6 +413,8 @@ type in Go are instead represented by a uintptr. Those include:
jobjectArray
jweak
3. The EGLDisplay type 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
pointers but data structures encoded in a pointer type. All operations
@ -427,6 +429,11 @@ 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
to auto-update code from Go 1.11 and earlier:
go tool fix -r egl <pkg>
Using cgo directly
Usage:
@ -827,6 +834,10 @@ The directives are:
possibly version in the dynamic library, and the optional "<library>"
names the specific library where the symbol should be found.
On AIX, the library pattern is slightly different. It must be
"lib.a/obj.o" with obj.o the member of this library exporting
this symbol.
In the <remote>, # or @ can be used to introduce a symbol version.
Examples:

File diff suppressed because it is too large Load Diff

View File

@ -126,3 +126,9 @@ func gofmt(n interface{}) string {
}
return gofmtBuf.String()
}
// gofmtLine returns the gofmt-formatted string for an AST node,
// ensuring that it is on a single line.
func gofmtLine(n interface{}) string {
return strings.Replace(gofmt(n), "\n", ";", -1)
}

View File

@ -47,7 +47,14 @@ type Package struct {
GccFiles []string // list of gcc output files
Preamble string // collected preamble for _cgo_export.h
typedefs map[string]bool // type names that appear in the types of the objects we're interested in
typedefList []string
typedefList []typedefInfo
}
// A typedefInfo is an element on Package.typedefList: a typedef name
// and the position where it was required.
type typedefInfo struct {
typedef string
pos token.Pos
}
// A File collects information about a single Go input file.
@ -64,6 +71,9 @@ type File struct {
Edit *edit.Buffer
}
// Untyped constants in the current package.
var consts = make(map[string]bool)
func (f *File) offset(p token.Pos) int {
return fset.Position(p).Offset
}
@ -81,6 +91,7 @@ func nameKeys(m map[string]*Name) []string {
type Call struct {
Call *ast.CallExpr
Deferred bool
Done bool
}
// A Ref refers to an expression of the form C.xxx in the AST.
@ -88,19 +99,22 @@ type Ref struct {
Name *Name
Expr *ast.Expr
Context astContext
Done bool
}
func (r *Ref) Pos() token.Pos {
return (*r.Expr).Pos()
}
var nameKinds = []string{"iconst", "fconst", "sconst", "type", "var", "fpvar", "func", "macro", "not-type"}
// A Name collects information about C.xxx.
type Name struct {
Go string // name used in Go referring to package C
Mangle string // name used in generated Go
C string // name used in C
Define string // #define expansion
Kind string // "iconst", "fconst", "sconst", "type", "var", "fpvar", "func", "macro", "not-type"
Kind string // one of the nameKinds
Type *Type // the type of xxx
FuncType *FuncType
AddError bool

View File

@ -9,11 +9,11 @@ import (
"debug/elf"
"debug/macho"
"debug/pe"
"debug/xcoff"
"fmt"
"go/ast"
"go/printer"
"go/token"
"internal/xcoff"
"io"
"io/ioutil"
"os"
@ -251,7 +251,22 @@ func (p *Package) writeDefs() {
init := gccgoInit.String()
if init != "" {
fmt.Fprintln(fc, "static void init(void) __attribute__ ((constructor));")
// The init function does nothing but simple
// assignments, so it won't use much stack space, so
// it's OK to not split the stack. Splitting the stack
// can run into a bug in clang (as of 2018-11-09):
// this is a leaf function, and when clang sees a leaf
// function it won't emit the split stack prologue for
// the function. However, if this function refers to a
// non-split-stack function, which will happen if the
// cgo code refers to a C function not compiled with
// -fsplit-stack, then the linker will think that it
// needs to adjust the split stack prologue, but there
// won't be one. Marking the function explicitly
// no_split_stack works around this problem by telling
// the linker that it's OK if there is no split stack
// prologue.
fmt.Fprintln(fc, "static void init(void) __attribute__ ((constructor, no_split_stack));")
fmt.Fprintln(fc, "static void init(void) {")
fmt.Fprint(fc, init)
fmt.Fprintln(fc, "}")
@ -1193,7 +1208,7 @@ func (p *Package) writeExportHeader(fgcch io.Writer) {
fmt.Fprintf(fgcch, "%s\n", p.gccExportHeaderProlog())
}
// gccgoUsesNewMangling returns whether gccgo uses the new collision-free
// gccgoUsesNewMangling reports whether gccgo uses the new collision-free
// packagepath mangling scheme (see determineGccgoManglingScheme for more
// info).
func gccgoUsesNewMangling() bool {
@ -1545,6 +1560,7 @@ const builtinProlog = `
/* Define intgo when compiling with GCC. */
typedef ptrdiff_t intgo;
#define GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; intgo n; } _GoString_;
typedef struct { char *p; intgo n; intgo c; } _GoBytes_;
_GoString_ GoString(char *p);
@ -1555,7 +1571,7 @@ void *CBytes(_GoBytes_);
void *_CMalloc(size_t);
__attribute__ ((unused))
static size_t _GoStringLen(_GoString_ s) { return s.n; }
static size_t _GoStringLen(_GoString_ s) { return (size_t)s.n; }
__attribute__ ((unused))
static const char *_GoStringPtr(_GoString_ s) { return s.p; }
@ -1796,15 +1812,20 @@ void localCgoCheckResult(Eface val) {
// because _cgo_export.h defines GoString as a struct while builtinProlog
// defines it as a function. We don't change this to avoid unnecessarily
// breaking existing code.
// The test of GO_CGO_GOSTRING_TYPEDEF avoids a duplicate definition
// error if a Go file with a cgo comment #include's the export header
// generated by a different package.
const builtinExportProlog = `
#line 1 "cgo-builtin-prolog"
#line 1 "cgo-builtin-export-prolog"
#include <stddef.h> /* for ptrdiff_t below */
#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
#endif
#endif
`
@ -1813,6 +1834,19 @@ func (p *Package) gccExportHeaderProlog() string {
return strings.Replace(gccExportHeaderProlog, "GOINTBITS", fmt.Sprint(8*p.IntSize), -1)
}
// gccExportHeaderProlog is written to the exported header, after the
// import "C" comment preamble but before the generated declarations
// of exported functions. This permits the generated declarations to
// use the type names that appear in goTypes, above.
//
// The test of GO_CGO_GOSTRING_TYPEDEF avoids a duplicate definition
// error if a Go file with a cgo comment #include's the export header
// generated by a different package. Unfortunately GoString means two
// different things: in this prolog it means a C name for the Go type,
// while in the prolog written into the start of the C code generated
// from a cgo-using Go file it means the C.GoString function. There is
// no way to resolve this conflict, but it also doesn't make much
// difference, as Go code never wants to refer to the latter meaning.
const gccExportHeaderProlog = `
/* Start of boilerplate cgo prologue. */
#line 1 "cgo-gcc-export-header-prolog"
@ -1842,7 +1876,9 @@ typedef double _Complex GoComplex128;
*/
typedef char _check_for_GOINTBITS_bit_pointer_matching_GoInt[sizeof(void*)==GOINTBITS/8 ? 1:-1];
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;

View File

@ -144,7 +144,7 @@
// link against shared libraries previously created with
// -buildmode=shared.
// -mod mode
// module download mode to use: readonly, release, or vendor.
// module download mode to use: readonly or vendor.
// See 'go help modules' for more.
// -pkgdir dir
// install and load all packages from dir instead of the usual locations.
@ -342,12 +342,21 @@
// cd go/src/encoding/json; go doc decode
//
// Flags:
// -all
// Show all the documentation for the package.
// -c
// Respect case when matching symbols.
// -cmd
// Treat a command (package main) like a regular package.
// Otherwise package main's exported symbols are hidden
// when showing the package's top-level documentation.
// -src
// Show the full source code for the symbol. This will
// display the full Go source of its declaration and
// definition, such as a function definition (including
// the body), type declaration or enclosing const
// block. The output may therefore include unexported
// details.
// -u
// Show documentation for unexported as well as exported
// symbols, methods, and fields.
@ -889,7 +898,7 @@
//
// Usage:
//
// go mod download [-dir] [-json] [modules]
// go mod download [-json] [modules]
//
// Download downloads the named modules, which can be module patterns selecting
// dependencies of the main module or module queries of the form path@version.
@ -963,6 +972,8 @@
// and -dropreplace editing flags may be repeated, and the changes
// are applied in the order given.
//
// The -go=version flag sets the expected Go language version.
//
// The -print flag prints the final go.mod in its text format instead of
// writing it back to go.mod.
//
@ -975,7 +986,8 @@
// }
//
// type GoMod struct {
// Module Module
// Module Module
// Go string
// Require []Require
// Exclude []Module
// Replace []Replace
@ -1287,16 +1299,25 @@
//
// Usage:
//
// go vet [-n] [-x] [build flags] [vet flags] [packages]
// go vet [-n] [-x] [-vettool prog] [build flags] [vet flags] [packages]
//
// Vet runs the Go vet command on the packages named by the import paths.
//
// For more about vet and its flags, see 'go doc cmd/vet'.
// For more about specifying packages, see 'go help packages'.
// For a list of checkers and their flags, see 'go tool vet help'.
// For details of a specific checker such as 'printf', see 'go tool vet help printf'.
//
// The -n flag prints commands that would be executed.
// The -x flag prints commands as they are executed.
//
// The -vettool=prog flag selects a different analysis tool with alternative
// or additional checks.
// For example, the 'shadow' analyzer can be built and run using these commands:
//
// go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
// go vet -vettool=$(which shadow)
//
// The build flags supported by go vet are those that control package resolution
// and execution, such as -n, -x, -v, -tags, and -toolexec.
// For more about these flags, see 'go help build'.
@ -1451,9 +1472,7 @@
//
// Each entry in the GOFLAGS list must be a standalone flag.
// Because the entries are space-separated, flag values must
// not contain spaces. In some cases, you can provide multiple flag
// values instead: for example, to set '-ldflags=-s -w'
// you can use 'GOFLAGS=-ldflags=-s -ldflags=-w'.
// not contain spaces.
//
// Environment variables for use with cgo:
//
@ -1488,6 +1507,10 @@
// The command to use to compile C++ code.
// PKG_CONFIG
// Path to pkg-config tool.
// AR
// The command to use to manipulate library archives when
// building with the gccgo compiler.
// The default is 'ar'.
//
// Architecture-specific environment variables:
//
@ -1595,17 +1618,20 @@
// verb followed by arguments. For example:
//
// module my/thing
// go 1.12
// require other/thing v1.0.2
// require new/thing v2.3.4
// require new/thing/v2 v2.3.4
// exclude old/thing v1.2.3
// replace bad/thing v1.4.5 => good/thing v1.4.5
//
// The verbs are module, to define the module path; require, to require
// a particular module at a given version or later; exclude, to exclude
// a particular module version from use; and replace, to replace a module
// version with a different module version. Exclude and replace apply only
// in the main module's go.mod and are ignored in dependencies.
// See https://research.swtch.com/vgo-mvs for details.
// The verbs are
// module, to define the module path;
// go, to set the expected language version;
// require, to require a particular module at a given version or later;
// exclude, to exclude a particular module version from use; and
// replace, to replace a module version with a different module version.
// Exclude and replace apply only in the main module's go.mod and are ignored
// in dependencies. See https://research.swtch.com/vgo-mvs for details.
//
// The leading verb can be factored out of adjacent lines to create a block,
// like in Go imports:
@ -2028,7 +2054,7 @@
// (See 'go help gopath-get' and 'go help gopath'.)
//
// When using modules, downloaded packages are stored in the module cache.
// (See 'go help modules-get' and 'go help goproxy'.)
// (See 'go help module-get' and 'go help goproxy'.)
//
// When using modules, an additional variant of the go-import meta tag is
// recognized and is preferred over those listing version control systems.
@ -2490,7 +2516,7 @@
// In general, adding a new dependency may require upgrading
// existing dependencies to keep a working build, and 'go get' does
// this automatically. Similarly, downgrading one dependency may
// require downgrading other dependenceis, and 'go get' does
// require downgrading other dependencies, and 'go get' does
// this automatically as well.
//
// The -m flag instructs get to stop here, after resolving, upgrading,
@ -2652,6 +2678,8 @@
// Run enough iterations of each benchmark to take t, specified
// as a time.Duration (for example, -benchtime 1h30s).
// The default is 1 second (1s).
// The special syntax Nx means to run the benchmark N times
// (for example, -benchtime 100x).
//
// -count n
// Run each test and benchmark n times (default 1).

View File

@ -6,6 +6,8 @@ package main_test
import (
"bytes"
"cmd/internal/sys"
"context"
"debug/elf"
"debug/macho"
"flag"
@ -107,6 +109,12 @@ var testGo string
var testTmpDir string
var testBin string
// testCtx is canceled when the test binary is about to time out.
//
// If https://golang.org/issue/28135 is accepted, uses of this variable in test
// functions should be replaced by t.Context().
var testCtx = context.Background()
// The TestMain function creates a go command for testing purposes and
// deletes it after the tests have been run.
func TestMain(m *testing.M) {
@ -119,6 +127,20 @@ func TestMain(m *testing.M) {
os.Unsetenv("GOROOT_FINAL")
flag.Parse()
timeoutFlag := flag.Lookup("test.timeout")
if timeoutFlag != nil {
// TODO(golang.org/issue/28147): The go command does not pass the
// test.timeout flag unless either -timeout or -test.timeout is explicitly
// set on the command line.
if d := timeoutFlag.Value.(flag.Getter).Get().(time.Duration); d != 0 {
aBitShorter := d * 95 / 100
var cancel context.CancelFunc
testCtx, cancel = context.WithTimeout(testCtx, aBitShorter)
defer cancel()
}
}
if *proxyAddr != "" {
StartProxy()
select {}
@ -209,15 +231,13 @@ func TestMain(m *testing.M) {
}
testGOCACHE = strings.TrimSpace(string(out))
// As of Sept 2017, MSan is only supported on linux/amd64.
// https://github.com/google/sanitizers/wiki/MemorySanitizer#getting-memorysanitizer
canMSan = canCgo && runtime.GOOS == "linux" && runtime.GOARCH == "amd64"
switch runtime.GOOS {
case "linux", "darwin", "freebsd", "windows":
// The race detector doesn't work on Alpine Linux:
// golang.org/issue/14481
canRace = canCgo && runtime.GOARCH == "amd64" && !isAlpineLinux() && runtime.Compiler != "gccgo"
canMSan = canCgo && sys.MSanSupported(runtime.GOOS, runtime.GOARCH)
canRace = canCgo && sys.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH)
// The race detector doesn't work on Alpine Linux:
// golang.org/issue/14481
// gccgo does not support the race detector.
if isAlpineLinux() || runtime.Compiler == "gccgo" {
canRace = false
}
}
// Don't let these environment variables confuse the test.
@ -1075,6 +1095,8 @@ func testMove(t *testing.T, vcs, url, base, config string) {
defer tg.cleanup()
tg.parallel()
tg.tempDir("src")
tg.must(os.Mkdir(tg.path(".hg"), 0700))
tg.must(ioutil.WriteFile(filepath.Join(tg.path(".hg"), "hgrc"), nil, 0600))
tg.setenv("GOPATH", tg.path("."))
tg.run("get", "-d", url)
tg.run("get", "-d", "-u", url)
@ -1089,7 +1111,7 @@ func testMove(t *testing.T, vcs, url, base, config string) {
path := tg.path(filepath.Join("src", config))
data, err := ioutil.ReadFile(path)
tg.must(err)
data = bytes.Replace(data, []byte(base), []byte(base+"XXX"), -1)
data = bytes.ReplaceAll(data, []byte(base), []byte(base+"XXX"))
tg.must(ioutil.WriteFile(path, data, 0644))
}
if vcs == "git" {
@ -1185,22 +1207,6 @@ func TestImportCycle(t *testing.T) {
tg.run("list", "-e", "-json", "selfimport")
}
func TestListImportMap(t *testing.T) {
skipIfGccgo(t, "gccgo does not have standard packages")
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.run("list", "-f", "{{.ImportPath}}: {{.ImportMap}}", "net", "fmt")
tg.grepStdout(`^net: map\[(.* )?golang_org/x/net/dns/dnsmessage:vendor/golang_org/x/net/dns/dnsmessage.*\]`, "net/http should have rewritten dnsmessage import")
tg.grepStdout(`^fmt: map\[\]`, "fmt should have no rewritten imports")
tg.run("list", "-deps", "-test", "-f", "{{.ImportPath}} MAP: {{.ImportMap}}\n{{.ImportPath}} IMPORT: {{.Imports}}", "fmt")
tg.grepStdout(`^flag \[fmt\.test\] MAP: map\[fmt:fmt \[fmt\.test\]\]`, "flag [fmt.test] should import fmt [fmt.test] as fmt")
tg.grepStdout(`^fmt\.test MAP: map\[(.* )?testing:testing \[fmt\.test\]`, "fmt.test should import testing [fmt.test] as testing")
tg.grepStdout(`^fmt\.test MAP: map\[(.* )?testing:testing \[fmt\.test\]`, "fmt.test should import testing [fmt.test] as testing")
tg.grepStdoutNot(`^fmt\.test MAP: map\[(.* )?os:`, "fmt.test should not import a modified os")
tg.grepStdout(`^fmt\.test IMPORT: \[fmt \[fmt\.test\] fmt_test \[fmt\.test\] os testing \[fmt\.test\] testing/internal/testdeps \[fmt\.test\]\]`, "wrong imports for fmt.test")
}
// cmd/go: custom import path checking should not apply to Go packages without import comment.
func TestIssue10952(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
@ -1421,6 +1427,7 @@ func TestRelativeGOBINFail(t *testing.T) {
tg.tempFile("triv.go", `package main; func main() {}`)
tg.cd(tg.path("."))
tg.setenv("GOBIN", ".")
tg.cd(tg.path("."))
tg.runFail("install")
tg.grepStderr("cannot install, GOBIN must be an absolute path", "go install must fail if $GOBIN is a relative path")
}
@ -1440,8 +1447,38 @@ func TestInstallIntoGOPATH(t *testing.T) {
func TestBuildOutputToDevNull(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
fi1, err1 := os.Lstat(os.DevNull)
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.run("build", "-o", os.DevNull, "go-cmd-test")
fi2, err2 := os.Lstat(os.DevNull)
if err1 == nil {
if err2 != nil {
t.Errorf("second stat of /dev/null failed: %v", err2)
} else if !os.SameFile(fi1, fi2) {
t.Errorf("/dev/null changed: now %v was %v", fi1, fi2)
}
}
}
// Issue 28549.
func TestTestOutputToDevNull(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
fi1, err1 := os.Lstat(os.DevNull)
tg.makeTempdir()
tg.setenv("GOPATH", tg.path("."))
tg.tempFile("src/p/p.go", "package p\n")
tg.tempFile("src/p/p_test.go", "package p\nimport \"testing\"\nfunc TestX(t *testing.T) {}\n")
tg.run("test", "-o", os.DevNull, "-c", "p")
tg.mustNotExist("p.test")
fi2, err2 := os.Lstat(os.DevNull)
if err1 == nil {
if err2 != nil {
t.Errorf("second stat of /dev/null failed: %v", err2)
} else if !os.SameFile(fi1, fi2) {
t.Errorf("/dev/null changed: now %v was %v", fi1, fi2)
}
}
}
func TestPackageMainTestImportsArchiveNotBinary(t *testing.T) {
@ -1735,11 +1772,11 @@ func TestGoListDeps(t *testing.T) {
if runtime.Compiler != "gccgo" {
// Check the list is in dependency order.
tg.run("list", "-deps", "math")
want := "internal/cpu\nunsafe\nmath\n"
want := "internal/cpu\nunsafe\nmath/bits\nmath\n"
out := tg.stdout.String()
if !strings.Contains(out, "internal/cpu") {
// Some systems don't use internal/cpu.
want = "unsafe\nmath\n"
want = "unsafe\nmath/bits\nmath\n"
}
if tg.stdout.String() != want {
t.Fatalf("list -deps math: wrong order\nhave %q\nwant %q", tg.stdout.String(), want)
@ -2359,14 +2396,14 @@ func TestShadowingLogic(t *testing.T) {
// The math in root1 is not "math" because the standard math is.
tg.run("list", "-f", "({{.ImportPath}}) ({{.ConflictDir}})", "./testdata/shadow/root1/src/math")
pwdForwardSlash := strings.Replace(pwd, string(os.PathSeparator), "/", -1)
pwdForwardSlash := strings.ReplaceAll(pwd, string(os.PathSeparator), "/")
if !strings.HasPrefix(pwdForwardSlash, "/") {
pwdForwardSlash = "/" + pwdForwardSlash
}
// The output will have makeImportValid applies, but we only
// bother to deal with characters we might reasonably see.
for _, r := range " :" {
pwdForwardSlash = strings.Replace(pwdForwardSlash, string(r), "_", -1)
pwdForwardSlash = strings.ReplaceAll(pwdForwardSlash, string(r), "_")
}
want := "(_" + pwdForwardSlash + "/testdata/shadow/root1/src/math) (" + filepath.Join(runtime.GOROOT(), "src", "math") + ")"
if strings.TrimSpace(tg.getStdout()) != want {
@ -2556,7 +2593,7 @@ func TestCoverageErrorLine(t *testing.T) {
// It's OK that stderr2 drops the character position in the error,
// because of the //line directive (see golang.org/issue/22662).
stderr = strings.Replace(stderr, "p.go:4:2:", "p.go:4:", -1)
stderr = strings.ReplaceAll(stderr, "p.go:4:2:", "p.go:4:")
if stderr != stderr2 {
t.Logf("test -cover changed error messages:\nbefore:\n%s\n\nafter:\n%s", stderr, stderr2)
t.Skip("golang.org/issue/22660")
@ -4027,8 +4064,6 @@ func TestCgoConsistentResults(t *testing.T) {
t.Skip("skipping because cgo not enabled")
}
switch runtime.GOOS {
case "freebsd":
testenv.SkipFlaky(t, 15405)
case "solaris":
testenv.SkipFlaky(t, 13247)
}
@ -4977,7 +5012,8 @@ func TestExecBuildX(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.setenv("GOCACHE", "off")
tg.tempDir("cache")
tg.setenv("GOCACHE", tg.path("cache"))
tg.tempFile("main.go", `package main; import "C"; func main() { print("hello") }`)
src := tg.path("main.go")
@ -5508,30 +5544,6 @@ func TestTestCacheInputs(t *testing.T) {
}
}
func TestNoCache(t *testing.T) {
switch runtime.GOOS {
case "windows":
t.Skipf("no unwritable directories on %s", runtime.GOOS)
}
if os.Getuid() == 0 {
t.Skip("skipping test because running as root")
}
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.tempFile("triv.go", `package main; func main() {}`)
tg.must(os.MkdirAll(tg.path("unwritable"), 0555))
home := "HOME"
if runtime.GOOS == "plan9" {
home = "home"
}
tg.setenv(home, tg.path(filepath.Join("unwritable", "home")))
tg.unsetenv("GOCACHE")
tg.run("build", "-o", tg.path("triv"), tg.path("triv.go"))
tg.grepStderr("disabling cache", "did not disable cache")
}
func TestTestVet(t *testing.T) {
tooSlow(t)
tg := testgo(t)
@ -5681,17 +5693,6 @@ func TestFmtLoadErrors(t *testing.T) {
tg.run("fmt", "-n", "exclude")
}
func TestRelativePkgdir(t *testing.T) {
tooSlow(t)
tg := testgo(t)
defer tg.cleanup()
tg.makeTempdir()
tg.setenv("GOCACHE", "off")
tg.cd(tg.tempdir)
tg.run("build", "-i", "-pkgdir=.", "runtime")
}
func TestGoTestMinusN(t *testing.T) {
// Intent here is to verify that 'go test -n' works without crashing.
// This reuses flag_test.go, but really any test would do.
@ -6073,28 +6074,6 @@ func TestDontReportRemoveOfEmptyDir(t *testing.T) {
}
}
// Issue 23264.
func TestNoRelativeTmpdir(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.tempFile("src/a/a.go", `package a`)
tg.cd(tg.path("."))
tg.must(os.Mkdir("tmp", 0777))
tg.setenv("GOCACHE", "off")
tg.setenv("GOPATH", tg.path("."))
tg.setenv("GOTMPDIR", "tmp")
tg.run("build", "-work", "a")
tg.grepStderr("WORK=[^t]", "work should be absolute path")
tg.unsetenv("GOTMPDIR")
tg.setenv("TMP", "tmp") // windows
tg.setenv("TMPDIR", "tmp") // unix
tg.run("build", "-work", "a")
tg.grepStderr("WORK=[^t]", "work should be absolute path")
}
// Issue 24704.
func TestLinkerTmpDirIsDeleted(t *testing.T) {
skipIfGccgo(t, "gccgo does not use cmd/link")
@ -6172,7 +6151,7 @@ func TestCDAndGOPATHAreDifferent(t *testing.T) {
testCDAndGOPATHAreDifferent(tg, cd, gopath)
if runtime.GOOS == "windows" {
testCDAndGOPATHAreDifferent(tg, cd, strings.Replace(gopath, `\`, `/`, -1))
testCDAndGOPATHAreDifferent(tg, cd, strings.ReplaceAll(gopath, `\`, `/`))
testCDAndGOPATHAreDifferent(tg, cd, strings.ToUpper(gopath))
testCDAndGOPATHAreDifferent(tg, cd, strings.ToLower(gopath))
}

View File

@ -0,0 +1,28 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !nacl
package main_test
import (
"bytes"
"io/ioutil"
"testing"
"cmd/go/internal/help"
)
func TestDocsUpToDate(t *testing.T) {
buf := new(bytes.Buffer)
// Match the command in mkalldocs.sh that generates alldocs.go.
help.Help(buf, []string{"documentation"})
data, err := ioutil.ReadFile("alldocs.go")
if err != nil {
t.Fatalf("error reading alldocs.go: %v", err)
}
if !bytes.Equal(data, buf.Bytes()) {
t.Errorf("alldocs.go is not up to date; run mkalldocs.sh to regenerate it")
}
}

View File

@ -18,6 +18,8 @@ import (
"strconv"
"strings"
"time"
"cmd/go/internal/renameio"
)
// An ActionID is a cache action key, the hash of a complete description of a
@ -283,7 +285,9 @@ func (c *Cache) Trim() {
c.trimSubdir(subdir, cutoff)
}
ioutil.WriteFile(filepath.Join(c.dir, "trim.txt"), []byte(fmt.Sprintf("%d", now.Unix())), 0666)
// Ignore errors from here: if we don't write the complete timestamp, the
// cache will appear older than it is, and we'll trim it again next time.
renameio.WriteFile(filepath.Join(c.dir, "trim.txt"), []byte(fmt.Sprintf("%d", now.Unix())))
}
// trimSubdir trims a single cache subdirectory.
@ -338,6 +342,8 @@ func (c *Cache) putIndexEntry(id ActionID, out OutputID, size int64, allowVerify
}
file := c.fileName(id, "a")
if err := ioutil.WriteFile(file, entry, 0666); err != nil {
// TODO(bcmills): This Remove potentially races with another go command writing to file.
// Can we eliminate it?
os.Remove(file)
return err
}

View File

@ -9,8 +9,9 @@ import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"sync"
"cmd/go/internal/base"
)
// Default returns the default cache to use, or nil if no cache should be used.
@ -35,15 +36,15 @@ See golang.org to learn more about Go.
// initDefaultCache does the work of finding the default cache
// the first time Default is called.
func initDefaultCache() {
dir, showWarnings := defaultDir()
if dir == "off" {
return
dir := DefaultDir()
if dir == "off" || dir == "" {
if defaultDirErr != nil {
base.Fatalf("build cache is required, but could not be located: %v", defaultDirErr)
}
base.Fatalf("build cache is disabled by GOCACHE=off, but required as of Go 1.12")
}
if err := os.MkdirAll(dir, 0777); err != nil {
if showWarnings {
fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err)
}
return
base.Fatalf("failed to initialize build cache at %s: %s\n", dir, err)
}
if _, err := os.Stat(filepath.Join(dir, "README")); err != nil {
// Best effort.
@ -52,78 +53,40 @@ func initDefaultCache() {
c, err := Open(dir)
if err != nil {
if showWarnings {
fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err)
}
return
base.Fatalf("failed to initialize build cache at %s: %s\n", dir, err)
}
defaultCache = c
}
var (
defaultDirOnce sync.Once
defaultDir string
defaultDirErr error
)
// DefaultDir returns the effective GOCACHE setting.
// It returns "off" if the cache is disabled.
func DefaultDir() string {
dir, _ := defaultDir()
return dir
}
// defaultDir returns the effective GOCACHE setting.
// It returns "off" if the cache is disabled.
// The second return value reports whether warnings should
// be shown if the cache fails to initialize.
func defaultDir() (string, bool) {
dir := os.Getenv("GOCACHE")
if dir != "" {
return dir, true
}
// Compute default location.
// TODO(rsc): This code belongs somewhere else,
// like maybe ioutil.CacheDir or os.CacheDir.
showWarnings := true
switch runtime.GOOS {
case "windows":
dir = os.Getenv("LocalAppData")
if dir == "" {
// Fall back to %AppData%, the old name of
// %LocalAppData% on Windows XP.
dir = os.Getenv("AppData")
}
if dir == "" {
return "off", true
}
case "darwin":
dir = os.Getenv("HOME")
if dir == "" {
return "off", true
}
dir += "/Library/Caches"
case "plan9":
dir = os.Getenv("home")
if dir == "" {
return "off", true
}
// Plan 9 has no established per-user cache directory,
// but $home/lib/xyz is the usual equivalent of $HOME/.xyz on Unix.
dir += "/lib/cache"
default: // Unix
// https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
dir = os.Getenv("XDG_CACHE_HOME")
if dir == "" {
dir = os.Getenv("HOME")
if dir == "" {
return "off", true
}
if dir == "/" {
// probably docker run with -u flag
// https://golang.org/issue/26280
showWarnings = false
}
dir += "/.cache"
}
}
return filepath.Join(dir, "go-build"), showWarnings
// Save the result of the first call to DefaultDir for later use in
// initDefaultCache. cmd/go/main.go explicitly sets GOCACHE so that
// subprocesses will inherit it, but that means initDefaultCache can't
// otherwise distinguish between an explicit "off" and a UserCacheDir error.
defaultDirOnce.Do(func() {
defaultDir = os.Getenv("GOCACHE")
if defaultDir != "" {
return
}
// Compute default location.
dir, err := os.UserCacheDir()
if err != nil {
defaultDir = "off"
defaultDirErr = fmt.Errorf("GOCACHE is not defined and %v", err)
return
}
defaultDir = filepath.Join(dir, "go-build")
})
return defaultDir
}

View File

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

View File

@ -123,7 +123,7 @@ var hashFileCache struct {
m map[string][HashSize]byte
}
// HashFile returns the hash of the named file.
// FileHash returns the hash of the named file.
// It caches repeated lookups for a given file,
// and the cache entry for a file can be initialized
// using SetFileHash.

View File

@ -10,6 +10,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"time"
@ -17,6 +18,7 @@ import (
"cmd/go/internal/cache"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/lockedfile"
"cmd/go/internal/modfetch"
"cmd/go/internal/modload"
"cmd/go/internal/work"
@ -103,18 +105,16 @@ func init() {
}
func runClean(cmd *base.Command, args []string) {
if len(args) == 0 && modload.Failed() {
// Don't try to clean current directory,
// which will cause modload to base.Fatalf.
} else {
if len(args) > 0 || !modload.Enabled() || modload.HasModRoot() {
for _, pkg := range load.PackagesAndErrors(args) {
clean(pkg)
}
}
var b work.Builder
b.Print = fmt.Print
if cleanCache {
var b work.Builder
b.Print = fmt.Print
dir := cache.DefaultDir()
if dir != "off" {
// Remove the cache subdirectories but not the top cache directory.
@ -145,7 +145,20 @@ func runClean(cmd *base.Command, args []string) {
// right now are to be ignored.
dir := cache.DefaultDir()
if dir != "off" {
err := ioutil.WriteFile(filepath.Join(dir, "testexpire.txt"), []byte(fmt.Sprintf("%d\n", time.Now().UnixNano())), 0666)
f, err := lockedfile.Edit(filepath.Join(dir, "testexpire.txt"))
if err == nil {
now := time.Now().UnixNano()
buf, _ := ioutil.ReadAll(f)
prev, _ := strconv.ParseInt(strings.TrimSpace(string(buf)), 10, 64)
if now > prev {
if err = f.Truncate(0); err == nil {
_, err = fmt.Fprintf(f, "%d\n", now)
}
}
if closeErr := f.Close(); err == nil {
err = closeErr
}
}
if err != nil {
base.Errorf("go clean -testcache: %v", err)
}
@ -156,26 +169,17 @@ func runClean(cmd *base.Command, args []string) {
if modfetch.PkgMod == "" {
base.Fatalf("go clean -modcache: no module cache")
}
if err := removeAll(modfetch.PkgMod); err != nil {
base.Errorf("go clean -modcache: %v", err)
if cfg.BuildN || cfg.BuildX {
b.Showcmd("", "rm -rf %s", modfetch.PkgMod)
}
if !cfg.BuildN {
if err := modfetch.RemoveAll(modfetch.PkgMod); err != nil {
base.Errorf("go clean -modcache: %v", err)
}
}
}
}
func removeAll(dir string) error {
// Module cache has 0555 directories; make them writable in order to remove content.
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil // ignore errors walking in file system
}
if info.IsDir() {
os.Chmod(path, 0777)
}
return nil
})
return os.RemoveAll(dir)
}
var cleaned = map[*load.Package]bool{}
// TODO: These are dregs left by Makefile-based builds.

View File

@ -79,15 +79,15 @@ func AddKnownFlags(cmd string, defns []*Defn) {
// Parse sees if argument i is present in the definitions and if so,
// returns its definition, value, and whether it consumed an extra word.
// If the flag begins (cmd+".") it is ignored for the purpose of this function.
func Parse(cmd string, defns []*Defn, args []string, i int) (f *Defn, value string, extra bool) {
// 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":
base.Usage()
usage()
}
if arg == "" || arg[0] != '-' {
return

View File

@ -106,12 +106,21 @@ Examples:
cd go/src/encoding/json; go doc decode
Flags:
-all
Show all the documentation for the package.
-c
Respect case when matching symbols.
-cmd
Treat a command (package main) like a regular package.
Otherwise package main's exported symbols are hidden
when showing the package's top-level documentation.
-src
Show the full source code for the symbol. This will
display the full Go source of its declaration and
definition, such as a function definition (including
the body), type declaration or enclosing const
block. The output may therefore include unexported
details.
-u
Show documentation for unexported as well as exported
symbols, methods, and fields.

View File

@ -115,8 +115,10 @@ func findEnv(env []cfg.EnvVar, name string) string {
// ExtraEnvVars returns environment variables that should not leak into child processes.
func ExtraEnvVars() []cfg.EnvVar {
gomod := ""
if modload.Init(); modload.ModRoot != "" {
gomod = filepath.Join(modload.ModRoot, "go.mod")
if modload.HasModRoot() {
gomod = filepath.Join(modload.ModRoot(), "go.mod")
} else if modload.Enabled() {
gomod = os.DevNull
}
return []cfg.EnvVar{
{Name: "GOMOD", Value: gomod},
@ -203,7 +205,7 @@ func runEnv(cmd *base.Command, args []string) {
fmt.Printf("%s=\"%s\"\n", e.Name, e.Value)
case "plan9":
if strings.IndexByte(e.Value, '\x00') < 0 {
fmt.Printf("%s='%s'\n", e.Name, strings.Replace(e.Value, "'", "''", -1))
fmt.Printf("%s='%s'\n", e.Name, strings.ReplaceAll(e.Value, "'", "''"))
} else {
v := strings.Split(e.Value, "\x00")
fmt.Printf("%s=(", e.Name)

View File

@ -34,7 +34,7 @@ See also: go fmt, go vet.
func runFix(cmd *base.Command, args []string) {
printed := false
for _, pkg := range load.Packages(args) {
if modload.Enabled() && !pkg.Module.Main {
if modload.Enabled() && pkg.Module != nil && !pkg.Module.Main {
if !printed {
fmt.Fprintf(os.Stderr, "go: not fixing packages in dependency modules\n")
printed = true

View File

@ -161,7 +161,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) {
if modload.Enabled() && !pkg.Module.Main {
if modload.Enabled() && pkg.Module != nil && !pkg.Module.Main {
if !printed {
fmt.Fprintf(os.Stderr, "go: not generating in packages in dependency modules\n")
printed = true

View File

@ -232,7 +232,7 @@ var downloadCache = map[string]bool{}
var downloadRootCache = map[string]bool{}
// download runs the download half of the get command
// for the package named by the argument.
// for the package or pattern named by the argument.
func download(arg string, parent *load.Package, stk *load.ImportStack, mode int) {
if mode&load.ResolveImport != 0 {
// Caller is responsible for expanding vendor paths.
@ -402,6 +402,23 @@ func downloadPackage(p *load.Package) error {
security = web.Insecure
}
// p can be either a real package, or a pseudo-package whose “import path” is
// actually a wildcard pattern.
// Trim the path at the element containing the first wildcard,
// and hope that it applies to the wildcarded parts too.
// This makes 'go get rsc.io/pdf/...' work in a fresh GOPATH.
importPrefix := p.ImportPath
if i := strings.Index(importPrefix, "..."); i >= 0 {
slash := strings.LastIndexByte(importPrefix[:i], '/')
if slash < 0 {
return fmt.Errorf("cannot expand ... in %q", p.ImportPath)
}
importPrefix = importPrefix[:slash]
}
if err := CheckImportPath(importPrefix); err != nil {
return fmt.Errorf("%s: invalid import path: %v", p.ImportPath, err)
}
if p.Internal.Build.SrcRoot != "" {
// Directory exists. Look for checkout along path to src.
vcs, rootPath, err = vcsFromDir(p.Dir, p.Internal.Build.SrcRoot)
@ -421,7 +438,7 @@ func downloadPackage(p *load.Package) error {
}
repo = remote
if !*getF && err == nil {
if rr, err := RepoRootForImportPath(p.ImportPath, IgnoreMod, security); err == nil {
if rr, err := RepoRootForImportPath(importPrefix, IgnoreMod, security); err == nil {
repo := rr.Repo
if rr.vcs.resolveRepo != nil {
resolved, err := rr.vcs.resolveRepo(rr.vcs, dir, repo)
@ -438,7 +455,7 @@ func downloadPackage(p *load.Package) error {
} else {
// Analyze the import path to determine the version control system,
// repository, and the import path for the root of the repository.
rr, err := RepoRootForImportPath(p.ImportPath, IgnoreMod, security)
rr, err := RepoRootForImportPath(importPrefix, IgnoreMod, security)
if err != nil {
return err
}

View File

@ -0,0 +1,192 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package get
import (
"fmt"
"strings"
"unicode"
"unicode/utf8"
)
// The following functions are copied verbatim from cmd/go/internal/module/module.go,
// with a change to additionally reject Windows short-names,
// and one to accept arbitrary letters (golang.org/issue/29101).
//
// TODO(bcmills): After the call site for this function is backported,
// consolidate this back down to a single copy.
//
// NOTE: DO NOT MERGE THESE UNTIL WE DECIDE ABOUT ARBITRARY LETTERS IN MODULE MODE.
// CheckImportPath checks that an import path is valid.
func CheckImportPath(path string) error {
if err := checkPath(path, false); err != nil {
return fmt.Errorf("malformed import path %q: %v", path, err)
}
return nil
}
// checkPath checks that a general path is valid.
// It returns an error describing why but not mentioning path.
// Because these checks apply to both module paths and import paths,
// the caller is expected to add the "malformed ___ path %q: " prefix.
// fileName indicates whether the final element of the path is a file name
// (as opposed to a directory name).
func checkPath(path string, fileName bool) error {
if !utf8.ValidString(path) {
return fmt.Errorf("invalid UTF-8")
}
if path == "" {
return fmt.Errorf("empty string")
}
if strings.Contains(path, "..") {
return fmt.Errorf("double dot")
}
if strings.Contains(path, "//") {
return fmt.Errorf("double slash")
}
if path[len(path)-1] == '/' {
return fmt.Errorf("trailing slash")
}
elemStart := 0
for i, r := range path {
if r == '/' {
if err := checkElem(path[elemStart:i], fileName); err != nil {
return err
}
elemStart = i + 1
}
}
if err := checkElem(path[elemStart:], fileName); err != nil {
return err
}
return nil
}
// checkElem checks whether an individual path element is valid.
// fileName indicates whether the element is a file name (not a directory name).
func checkElem(elem string, fileName bool) error {
if elem == "" {
return fmt.Errorf("empty path element")
}
if strings.Count(elem, ".") == len(elem) {
return fmt.Errorf("invalid path element %q", elem)
}
if elem[0] == '.' && !fileName {
return fmt.Errorf("leading dot in path element")
}
if elem[len(elem)-1] == '.' {
return fmt.Errorf("trailing dot in path element")
}
charOK := pathOK
if fileName {
charOK = fileNameOK
}
for _, r := range elem {
if !charOK(r) {
return fmt.Errorf("invalid char %q", r)
}
}
// Windows disallows a bunch of path elements, sadly.
// See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
short := elem
if i := strings.Index(short, "."); i >= 0 {
short = short[:i]
}
for _, bad := range badWindowsNames {
if strings.EqualFold(bad, short) {
return fmt.Errorf("disallowed path element %q", elem)
}
}
// Reject path components that look like Windows short-names.
// Those usually end in a tilde followed by one or more ASCII digits.
if tilde := strings.LastIndexByte(short, '~'); tilde >= 0 && tilde < len(short)-1 {
suffix := short[tilde+1:]
suffixIsDigits := true
for _, r := range suffix {
if r < '0' || r > '9' {
suffixIsDigits = false
break
}
}
if suffixIsDigits {
return fmt.Errorf("trailing tilde and digits in path element")
}
}
return nil
}
// pathOK reports whether r can appear in an import path element.
//
// NOTE: This function DIVERGES from module mode pathOK by accepting Unicode letters.
func pathOK(r rune) bool {
if r < utf8.RuneSelf {
return r == '+' || r == '-' || r == '.' || r == '_' || r == '~' ||
'0' <= r && r <= '9' ||
'A' <= r && r <= 'Z' ||
'a' <= r && r <= 'z'
}
return unicode.IsLetter(r)
}
// fileNameOK reports whether r can appear in a file name.
// For now we allow all Unicode letters but otherwise limit to pathOK plus a few more punctuation characters.
// If we expand the set of allowed characters here, we have to
// work harder at detecting potential case-folding and normalization collisions.
// See note about "safe encoding" below.
func fileNameOK(r rune) bool {
if r < utf8.RuneSelf {
// Entire set of ASCII punctuation, from which we remove characters:
// ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
// We disallow some shell special characters: " ' * < > ? ` |
// (Note that some of those are disallowed by the Windows file system as well.)
// We also disallow path separators / : and \ (fileNameOK is only called on path element characters).
// We allow spaces (U+0020) in file names.
const allowed = "!#$%&()+,-.=@[]^_{}~ "
if '0' <= r && r <= '9' || 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' {
return true
}
for i := 0; i < len(allowed); i++ {
if rune(allowed[i]) == r {
return true
}
}
return false
}
// It may be OK to add more ASCII punctuation here, but only carefully.
// For example Windows disallows < > \, and macOS disallows :, so we must not allow those.
return unicode.IsLetter(r)
}
// badWindowsNames are the reserved file path elements on Windows.
// See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file
var badWindowsNames = []string{
"CON",
"PRN",
"AUX",
"NUL",
"COM1",
"COM2",
"COM3",
"COM4",
"COM5",
"COM6",
"COM7",
"COM8",
"COM9",
"LPT1",
"LPT2",
"LPT3",
"LPT4",
"LPT5",
"LPT6",
"LPT7",
"LPT8",
"LPT9",
}

View File

@ -647,14 +647,7 @@ const (
func RepoRootForImportPath(importPath string, mod ModuleMode, security web.SecurityMode) (*RepoRoot, error) {
rr, err := repoRootFromVCSPaths(importPath, "", security, vcsPaths)
if err == errUnknownSite {
// If there are wildcards, look up the thing before the wildcard,
// hoping it applies to the wildcarded parts too.
// This makes 'go get rsc.io/pdf/...' work in a fresh GOPATH.
lookup := strings.TrimSuffix(importPath, "/...")
if i := strings.Index(lookup, "/.../"); i >= 0 {
lookup = lookup[:i]
}
rr, err = repoRootForImportDynamic(lookup, mod, security)
rr, err = repoRootForImportDynamic(importPath, mod, security)
if err != nil {
err = fmt.Errorf("unrecognized import path %q (%v)", importPath, err)
}
@ -667,6 +660,7 @@ func RepoRootForImportPath(importPath string, mod ModuleMode, security web.Secur
}
}
// Should have been taken care of above, but make sure.
if err == nil && strings.Contains(importPath, "...") && strings.Contains(rr.Root, "...") {
// Do not allow wildcards in the repo root.
rr = nil
@ -903,16 +897,16 @@ type metaImport struct {
Prefix, VCS, RepoRoot string
}
func splitPathHasPrefix(path, prefix []string) bool {
if len(path) < len(prefix) {
// pathPrefix reports whether sub is a prefix of s,
// only considering entire path components.
func pathPrefix(s, sub string) bool {
// strings.HasPrefix is necessary but not sufficient.
if !strings.HasPrefix(s, sub) {
return false
}
for i, p := range prefix {
if path[i] != p {
return false
}
}
return true
// The remainder after the prefix must either be empty or start with a slash.
rem := s[len(sub):]
return rem == "" || rem[0] == '/'
}
// A ImportMismatchError is returned where metaImport/s are present
@ -935,13 +929,10 @@ func (m ImportMismatchError) Error() string {
// errNoMatch is returned if none match.
func matchGoImport(imports []metaImport, importPath string) (metaImport, error) {
match := -1
imp := strings.Split(importPath, "/")
errImportMismatch := ImportMismatchError{importPath: importPath}
for i, im := range imports {
pre := strings.Split(im.Prefix, "/")
if !splitPathHasPrefix(imp, pre) {
if !pathPrefix(importPath, im.Prefix) {
errImportMismatch.mismatches = append(errImportMismatch.mismatches, im.Prefix)
continue
}
@ -966,10 +957,14 @@ func matchGoImport(imports []metaImport, importPath string) (metaImport, error)
// expand rewrites s to replace {k} with match[k] for each key k in match.
func expand(match map[string]string, s string) string {
// We want to replace each match exactly once, and the result of expansion
// must not depend on the iteration order through the map.
// A strings.Replacer has exactly the properties we're looking for.
oldNew := make([]string, 0, 2*len(match))
for k, v := range match {
s = strings.Replace(s, "{"+k+"}", v, -1)
oldNew = append(oldNew, "{"+k+"}", v)
}
return s
return strings.NewReplacer(oldNew...).Replace(s)
}
// vcsPaths defines the meaning of import paths referring to

View File

@ -20,16 +20,16 @@ import (
)
// Help implements the 'help' command.
func Help(args []string) {
func Help(w io.Writer, args []string) {
// 'go help documentation' generates doc.go.
if len(args) == 1 && args[0] == "documentation" {
fmt.Println("// Copyright 2011 The Go Authors. All rights reserved.")
fmt.Println("// Use of this source code is governed by a BSD-style")
fmt.Println("// license that can be found in the LICENSE file.")
fmt.Println()
fmt.Println("// Code generated by mkalldocs.sh; DO NOT EDIT.")
fmt.Println("// Edit the documentation in other files and rerun mkalldocs.sh to generate this one.")
fmt.Println()
fmt.Fprintln(w, "// Copyright 2011 The Go Authors. All rights reserved.")
fmt.Fprintln(w, "// Use of this source code is governed by a BSD-style")
fmt.Fprintln(w, "// license that can be found in the LICENSE file.")
fmt.Fprintln(w)
fmt.Fprintln(w, "// Code generated by mkalldocs.sh; DO NOT EDIT.")
fmt.Fprintln(w, "// Edit the documentation in other files and rerun mkalldocs.sh to generate this one.")
fmt.Fprintln(w)
buf := new(bytes.Buffer)
PrintUsage(buf, base.Go)
usage := &base.Command{Long: buf.String()}
@ -42,8 +42,8 @@ func Help(args []string) {
cmds = append(cmds, cmd)
cmds = append(cmds, cmd.Commands...)
}
tmpl(&commentWriter{W: os.Stdout}, documentationTemplate, cmds)
fmt.Println("package main")
tmpl(&commentWriter{W: w}, documentationTemplate, cmds)
fmt.Fprintln(w, "package main")
return
}

View File

@ -266,7 +266,7 @@ listed in the GOPATH environment variable.
(See 'go help gopath-get' and 'go help gopath'.)
When using modules, downloaded packages are stored in the module cache.
(See 'go help modules-get' and 'go help goproxy'.)
(See 'go help module-get' and 'go help goproxy'.)
When using modules, an additional variant of the go-import meta tag is
recognized and is preferred over those listing version control systems.
@ -509,9 +509,7 @@ General-purpose environment variables:
Each entry in the GOFLAGS list must be a standalone flag.
Because the entries are space-separated, flag values must
not contain spaces. In some cases, you can provide multiple flag
values instead: for example, to set '-ldflags=-s -w'
you can use 'GOFLAGS=-ldflags=-s -ldflags=-w'.
not contain spaces.
Environment variables for use with cgo:
@ -546,6 +544,10 @@ Environment variables for use with cgo:
The command to use to compile C++ code.
PKG_CONFIG
Path to pkg-config tool.
AR
The command to use to manipulate library archives when
building with the gccgo compiler.
The default is 'ar'.
Architecture-specific environment variables:

View File

@ -207,5 +207,5 @@ func init() {
}
}
const goosList = "android darwin dragonfly freebsd js linux nacl netbsd openbsd plan9 solaris windows zos "
const goosList = "aix android darwin dragonfly freebsd hurd js linux nacl netbsd openbsd plan9 solaris windows zos "
const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc riscv riscv64 s390 s390x sparc sparc64 wasm "

View File

@ -22,6 +22,16 @@ func ScanDir(dir string, tags map[string]bool) ([]string, []string, error) {
var files []string
for _, info := range infos {
name := info.Name()
// If the directory entry is a symlink, stat it to obtain the info for the
// link target instead of the link itself.
if info.Mode()&os.ModeSymlink != 0 {
info, err = os.Stat(name)
if err != nil {
continue // Ignore broken symlinks.
}
}
if info.Mode().IsRegular() && !strings.HasPrefix(name, "_") && strings.HasSuffix(name, ".go") && MatchFile(name, tags) {
files = append(files, filepath.Join(dir, name))
}

View File

@ -440,6 +440,10 @@ const (
// this package, as part of a bigger load operation, and by GOPATH-based "go get".
// TODO(rsc): When GOPATH-based "go get" is removed, unexport this function.
func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPos []token.Position, mode int) *Package {
if path == "" {
panic("LoadImport called with empty package path")
}
stk.Push(path)
defer stk.Pop()
@ -999,10 +1003,12 @@ func disallowInternal(srcDir string, importer *Package, importerPath string, p *
} else {
// p is in a module, so make it available based on the importer's import path instead
// of the file path (https://golang.org/issue/23970).
if importerPath == "." {
if importer.Internal.CmdlineFiles {
// The importer is a list of command-line files.
// Pretend that the import path is the import path of the
// directory containing them.
// If the directory is outside the main module, this will resolve to ".",
// which is not a prefix of any valid module.
importerPath = ModDirImportPath(importer.Dir)
}
parentOfInternal := p.ImportPath[:i]
@ -1053,20 +1059,6 @@ func disallowVendor(srcDir string, importer *Package, importerPath, path string,
return p
}
// Modules must not import vendor packages in the standard library,
// but the usual vendor visibility check will not catch them
// because the module loader presents them with an ImportPath starting
// with "golang_org/" instead of "vendor/".
if p.Standard && !importer.Standard && strings.HasPrefix(p.ImportPath, "golang_org") {
perr := *p
perr.Error = &PackageError{
ImportStack: stk.Copy(),
Err: "use of vendored package " + path + " not allowed",
}
perr.Incomplete = true
return &perr
}
if perr := disallowVendorVisibility(srcDir, p, stk); perr != p {
return perr
}
@ -1345,6 +1337,7 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
// SWIG adds imports of some standard packages.
if p.UsesSwig() {
addImport("unsafe", true)
if cfg.BuildContext.Compiler != "gccgo" {
addImport("runtime/cgo", true)
}
@ -1530,9 +1523,13 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
}
if cfg.ModulesEnabled {
p.Module = ModPackageModuleInfo(p.ImportPath)
mainPath := p.ImportPath
if p.Internal.CmdlineFiles {
mainPath = "command-line-arguments"
}
p.Module = ModPackageModuleInfo(mainPath)
if p.Name == "main" {
p.Internal.BuildInfo = ModPackageBuildInfo(p.ImportPath, p.Deps)
p.Internal.BuildInfo = ModPackageBuildInfo(mainPath, p.Deps)
}
}
}
@ -1756,6 +1753,9 @@ func LoadPackageNoFlags(arg string, stk *ImportStack) *Package {
// loadPackage accepts pseudo-paths beginning with cmd/ to denote commands
// in the Go command directory, as well as paths to those directories.
func loadPackage(arg string, stk *ImportStack) *Package {
if arg == "" {
panic("loadPackage called with empty package path")
}
if build.IsLocalImport(arg) {
dir := arg
if !filepath.IsAbs(dir) {
@ -1779,9 +1779,6 @@ func loadPackage(arg string, stk *ImportStack) *Package {
bp.ImportPath = arg
bp.Goroot = true
bp.BinDir = cfg.GOROOTbin
if cfg.GOROOTbin != "" {
bp.BinDir = cfg.GOROOTbin
}
bp.Root = cfg.GOROOT
bp.SrcRoot = cfg.GOROOTsrc
p := new(Package)
@ -1854,6 +1851,9 @@ 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))
}
p := loadPackage(pkg, &stk)
p.Match = append(p.Match, m.Pattern)
p.Internal.CmdlinePkg = true
@ -1996,11 +1996,6 @@ func GoFilesPackage(gofiles []string) *Package {
}
bp, err := ctxt.ImportDir(dir, 0)
if ModDirImportPath != nil {
// Use the effective import path of the directory
// for deciding visibility during pkg.load.
bp.ImportPath = ModDirImportPath(dir)
}
pkg := new(Package)
pkg.Internal.Local = true
pkg.Internal.CmdlineFiles = true

View File

@ -227,6 +227,12 @@ func GetTestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Pac
}
}
allTestImports := make([]*Package, 0, len(pmain.Internal.Imports)+len(imports)+len(ximports))
allTestImports = append(allTestImports, pmain.Internal.Imports...)
allTestImports = append(allTestImports, imports...)
allTestImports = append(allTestImports, ximports...)
setToolFlags(allTestImports...)
// Do initial scan for metadata needed for writing _testmain.go
// Use that metadata to update the list of imports for package main.
// The list of imports is used by recompileForTest and by the loop

View File

@ -0,0 +1,98 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package filelock provides a platform-independent API for advisory file
// locking. Calls to functions in this package on platforms that do not support
// advisory locks will return errors for which IsNotSupported returns true.
package filelock
import (
"errors"
"os"
)
// A File provides the minimal set of methods required to lock an open file.
// File implementations must be usable as map keys.
// The usual implementation is *os.File.
type File interface {
// Name returns the name of the file.
Name() string
// Fd returns a valid file descriptor.
// (If the File is an *os.File, it must not be closed.)
Fd() uintptr
// Stat returns the FileInfo structure describing file.
Stat() (os.FileInfo, error)
}
// Lock places an advisory write lock on the file, blocking until it can be
// locked.
//
// If Lock returns nil, no other process will be able to place a read or write
// lock on the file until this process exits, closes f, or calls Unlock on it.
//
// If f's descriptor is already read- or write-locked, the behavior of Lock is
// unspecified.
//
// Closing the file may or may not release the lock promptly. Callers should
// ensure that Unlock is always called when Lock succeeds.
func Lock(f File) error {
return lock(f, writeLock)
}
// RLock places an advisory read lock on the file, blocking until it can be locked.
//
// If RLock returns nil, no other process will be able to place a write lock on
// the file until this process exits, closes f, or calls Unlock on it.
//
// If f is already read- or write-locked, the behavior of RLock is unspecified.
//
// Closing the file may or may not release the lock promptly. Callers should
// ensure that Unlock is always called if RLock succeeds.
func RLock(f File) error {
return lock(f, readLock)
}
// Unlock removes an advisory lock placed on f by this process.
//
// The caller must not attempt to unlock a file that is not locked.
func Unlock(f File) error {
return unlock(f)
}
// String returns the name of the function corresponding to lt
// (Lock, RLock, or Unlock).
func (lt lockType) String() string {
switch lt {
case readLock:
return "RLock"
case writeLock:
return "Lock"
default:
return "Unlock"
}
}
// IsNotSupported returns a boolean indicating whether the error is known to
// report that a function is not supported (possibly for a specific input).
// It is satisfied by ErrNotSupported as well as some syscall errors.
func IsNotSupported(err error) bool {
return isNotSupported(underlyingError(err))
}
var ErrNotSupported = errors.New("operation not supported")
// underlyingError returns the underlying error for known os error types.
func underlyingError(err error) error {
switch err := err.(type) {
case *os.PathError:
return err.Err
case *os.LinkError:
return err.Err
case *os.SyscallError:
return err.Err
}
return err
}

View File

@ -0,0 +1,159 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build aix solaris
// This code implements the filelock API using POSIX 'fcntl' locks, which attach
// to an (inode, process) pair rather than a file descriptor. To avoid unlocking
// files prematurely when the same file is opened through different descriptors,
// we allow only one read-lock at a time.
//
// Most platforms provide some alternative API, such as an 'flock' system call
// 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.
package filelock
import (
"errors"
"io"
"os"
"sync"
"syscall"
)
type lockType int16
const (
readLock lockType = syscall.F_RDLCK
writeLock lockType = syscall.F_WRLCK
)
type inode = uint64 // type of syscall.Stat_t.Ino
type inodeLock struct {
owner File
queue []<-chan File
}
type token struct{}
var (
mu sync.Mutex
inodes = map[File]inode{}
locks = map[inode]inodeLock{}
)
func lock(f File, lt lockType) (err error) {
// POSIX locks apply per inode and process, and the lock for an inode is
// released when *any* descriptor for that inode is closed. So we need to
// synchronize access to each inode internally, and must serialize lock and
// unlock calls that refer to the same inode through different descriptors.
fi, err := f.Stat()
if err != nil {
return err
}
ino := fi.Sys().(*syscall.Stat_t).Ino
mu.Lock()
if i, dup := inodes[f]; dup && i != ino {
mu.Unlock()
return &os.PathError{
Op: lt.String(),
Path: f.Name(),
Err: errors.New("inode for file changed since last Lock or RLock"),
}
}
inodes[f] = ino
var wait chan File
l := locks[ino]
if l.owner == f {
// This file already owns the lock, but the call may change its lock type.
} else if l.owner == nil {
// No owner: it's ours now.
l.owner = f
} else {
// Already owned: add a channel to wait on.
wait = make(chan File)
l.queue = append(l.queue, wait)
}
locks[ino] = l
mu.Unlock()
if wait != nil {
wait <- f
}
err = setlkw(f.Fd(), lt)
if err != nil {
unlock(f)
return &os.PathError{
Op: lt.String(),
Path: f.Name(),
Err: err,
}
}
return nil
}
func unlock(f File) error {
var owner File
mu.Lock()
ino, ok := inodes[f]
if ok {
owner = locks[ino].owner
}
mu.Unlock()
if owner != f {
panic("unlock called on a file that is not locked")
}
err := setlkw(f.Fd(), syscall.F_UNLCK)
mu.Lock()
l := locks[ino]
if len(l.queue) == 0 {
// No waiters: remove the map entry.
delete(locks, ino)
} else {
// The first waiter is sending us their file now.
// Receive it and update the queue.
l.owner = <-l.queue[0]
l.queue = l.queue[1:]
locks[ino] = l
}
delete(inodes, f)
mu.Unlock()
return err
}
// setlkw calls FcntlFlock with F_SETLKW for the entire file indicated by fd.
func setlkw(fd uintptr, lt lockType) error {
for {
err := syscall.FcntlFlock(fd, syscall.F_SETLKW, &syscall.Flock_t{
Type: int16(lt),
Whence: io.SeekStart,
Start: 0,
Len: 0, // All bytes.
})
if err != syscall.EINTR {
return err
}
}
}
func isNotSupported(err error) bool {
return err == syscall.ENOSYS || err == syscall.ENOTSUP || err == syscall.EOPNOTSUPP || err == ErrNotSupported
}

View File

@ -0,0 +1,36 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!plan9,!solaris,!windows
package filelock
import "os"
type lockType int8
const (
readLock = iota + 1
writeLock
)
func lock(f File, lt lockType) error {
return &os.PathError{
Op: lt.String(),
Path: f.Name(),
Err: ErrNotSupported,
}
}
func unlock(f File) error {
return &os.PathError{
Op: "Unlock",
Path: f.Name(),
Err: ErrNotSupported,
}
}
func isNotSupported(err error) bool {
return err == ErrNotSupported
}

View File

@ -0,0 +1,38 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build plan9
package filelock
import (
"os"
)
type lockType int8
const (
readLock = iota + 1
writeLock
)
func lock(f File, lt lockType) error {
return &os.PathError{
Op: lt.String(),
Path: f.Name(),
Err: ErrNotSupported,
}
}
func unlock(f File) error {
return &os.PathError{
Op: "Unlock",
Path: f.Name(),
Err: ErrNotSupported,
}
}
func isNotSupported(err error) bool {
return err == ErrNotSupported
}

View File

@ -0,0 +1,209 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !js,!nacl,!plan9
package filelock_test
import (
"fmt"
"internal/testenv"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
"time"
"cmd/go/internal/lockedfile/internal/filelock"
)
func lock(t *testing.T, f *os.File) {
t.Helper()
err := filelock.Lock(f)
t.Logf("Lock(fd %d) = %v", f.Fd(), err)
if err != nil {
t.Fail()
}
}
func rLock(t *testing.T, f *os.File) {
t.Helper()
err := filelock.RLock(f)
t.Logf("RLock(fd %d) = %v", f.Fd(), err)
if err != nil {
t.Fail()
}
}
func unlock(t *testing.T, f *os.File) {
t.Helper()
err := filelock.Unlock(f)
t.Logf("Unlock(fd %d) = %v", f.Fd(), err)
if err != nil {
t.Fail()
}
}
func mustTempFile(t *testing.T) (f *os.File, remove func()) {
t.Helper()
base := filepath.Base(t.Name())
f, err := ioutil.TempFile("", base)
if err != nil {
t.Fatalf(`ioutil.TempFile("", %q) = %v`, base, err)
}
t.Logf("fd %d = %s", f.Fd(), f.Name())
return f, func() {
f.Close()
os.Remove(f.Name())
}
}
func mustOpen(t *testing.T, name string) *os.File {
t.Helper()
f, err := os.OpenFile(name, os.O_RDWR, 0)
if err != nil {
t.Fatalf("os.Open(%q) = %v", name, err)
}
t.Logf("fd %d = os.Open(%q)", f.Fd(), name)
return f
}
const (
quiescent = 10 * time.Millisecond
probablyStillBlocked = 10 * time.Second
)
func mustBlock(t *testing.T, op string, f *os.File) (wait func(*testing.T)) {
t.Helper()
desc := fmt.Sprintf("%s(fd %d)", op, f.Fd())
done := make(chan struct{})
go func() {
t.Helper()
switch op {
case "Lock":
lock(t, f)
case "RLock":
rLock(t, f)
default:
panic("invalid op: " + op)
}
close(done)
}()
select {
case <-done:
t.Fatalf("%s unexpectedly did not block", desc)
return nil
case <-time.After(quiescent):
t.Logf("%s is blocked (as expected)", desc)
return func(t *testing.T) {
t.Helper()
select {
case <-time.After(probablyStillBlocked):
t.Fatalf("%s is unexpectedly still blocked", desc)
case <-done:
}
}
}
}
func TestLockExcludesLock(t *testing.T) {
t.Parallel()
f, remove := mustTempFile(t)
defer remove()
other := mustOpen(t, f.Name())
defer other.Close()
lock(t, f)
lockOther := mustBlock(t, "Lock", other)
unlock(t, f)
lockOther(t)
unlock(t, other)
}
func TestLockExcludesRLock(t *testing.T) {
t.Parallel()
f, remove := mustTempFile(t)
defer remove()
other := mustOpen(t, f.Name())
defer other.Close()
lock(t, f)
rLockOther := mustBlock(t, "RLock", other)
unlock(t, f)
rLockOther(t)
unlock(t, other)
}
func TestRLockExcludesOnlyLock(t *testing.T) {
t.Parallel()
f, remove := mustTempFile(t)
defer remove()
rLock(t, f)
f2 := mustOpen(t, f.Name())
defer f2.Close()
if runtime.GOOS == "solaris" || runtime.GOOS == "aix" {
// When using POSIX locks (as on Solaris), we can't safely read-lock the
// same inode through two different descriptors at the same time: when the
// first descriptor is closed, the second descriptor would still be open but
// silently unlocked. So a second RLock must block instead of proceeding.
lockF2 := mustBlock(t, "RLock", f2)
unlock(t, f)
lockF2(t)
} else {
rLock(t, f2)
}
other := mustOpen(t, f.Name())
defer other.Close()
lockOther := mustBlock(t, "Lock", other)
unlock(t, f2)
if runtime.GOOS != "solaris" && runtime.GOOS != "aix" {
unlock(t, f)
}
lockOther(t)
unlock(t, other)
}
func TestLockNotDroppedByExecCommand(t *testing.T) {
testenv.MustHaveExec(t)
f, remove := mustTempFile(t)
defer remove()
lock(t, f)
other := mustOpen(t, f.Name())
defer other.Close()
// Some kinds of file locks are dropped when a duplicated or forked file
// descriptor is unlocked. Double-check that the approach used by os/exec does
// not accidentally drop locks.
cmd := exec.Command(os.Args[0], "-test.run=^$")
if err := cmd.Run(); err != nil {
t.Fatalf("exec failed: %v", err)
}
lockOther := mustBlock(t, "Lock", other)
unlock(t, f)
lockOther(t)
unlock(t, other)
}

View File

@ -0,0 +1,44 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd linux netbsd openbsd
package filelock
import (
"os"
"syscall"
)
type lockType int16
const (
readLock lockType = syscall.LOCK_SH
writeLock lockType = syscall.LOCK_EX
)
func lock(f File, lt lockType) (err error) {
for {
err = syscall.Flock(int(f.Fd()), int(lt))
if err != syscall.EINTR {
break
}
}
if err != nil {
return &os.PathError{
Op: lt.String(),
Path: f.Name(),
Err: err,
}
}
return nil
}
func unlock(f File) error {
return lock(f, syscall.LOCK_UN)
}
func isNotSupported(err error) bool {
return err == syscall.ENOSYS || err == syscall.ENOTSUP || err == syscall.EOPNOTSUPP || err == ErrNotSupported
}

View File

@ -0,0 +1,66 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build windows
package filelock
import (
"internal/syscall/windows"
"os"
"syscall"
)
type lockType uint32
const (
readLock lockType = 0
writeLock lockType = windows.LOCKFILE_EXCLUSIVE_LOCK
)
const (
reserved = 0
allBytes = ^uint32(0)
)
func lock(f File, lt lockType) error {
// Per https://golang.org/issue/19098, “Programs currently expect the Fd
// method to return a handle that uses ordinary synchronous I/O.”
// However, LockFileEx still requires an OVERLAPPED structure,
// which contains the file offset of the beginning of the lock range.
// We want to lock the entire file, so we leave the offset as zero.
ol := new(syscall.Overlapped)
err := windows.LockFileEx(syscall.Handle(f.Fd()), uint32(lt), reserved, allBytes, allBytes, ol)
if err != nil {
return &os.PathError{
Op: lt.String(),
Path: f.Name(),
Err: err,
}
}
return nil
}
func unlock(f File) error {
ol := new(syscall.Overlapped)
err := windows.UnlockFileEx(syscall.Handle(f.Fd()), reserved, allBytes, allBytes, ol)
if err != nil {
return &os.PathError{
Op: "Unlock",
Path: f.Name(),
Err: err,
}
}
return nil
}
func isNotSupported(err error) bool {
switch err {
case windows.ERROR_NOT_SUPPORTED, windows.ERROR_CALL_NOT_IMPLEMENTED, ErrNotSupported:
return true
default:
return false
}
}

View File

@ -0,0 +1,122 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package lockedfile creates and manipulates files whose contents should only
// change atomically.
package lockedfile
import (
"fmt"
"io"
"io/ioutil"
"os"
"runtime"
)
// A File is a locked *os.File.
//
// Closing the file releases the lock.
//
// If the program exits while a file is locked, the operating system releases
// the lock but may not do so promptly: callers must ensure that all locked
// files are closed before exiting.
type File struct {
osFile
closed bool
}
// osFile embeds a *os.File while keeping the pointer itself unexported.
// (When we close a File, it must be the same file descriptor that we opened!)
type osFile struct {
*os.File
}
// OpenFile is like os.OpenFile, but returns a locked file.
// If flag includes os.O_WRONLY or os.O_RDWR, the file is write-locked;
// otherwise, it is read-locked.
func OpenFile(name string, flag int, perm os.FileMode) (*File, error) {
var (
f = new(File)
err error
)
f.osFile.File, err = openFile(name, flag, perm)
if err != nil {
return nil, err
}
// Although the operating system will drop locks for open files when the go
// command exits, we want to hold locks for as little time as possible, and we
// especially don't want to leave a file locked after we're done with it. Our
// Close method is what releases the locks, so use a finalizer to report
// missing Close calls on a best-effort basis.
runtime.SetFinalizer(f, func(f *File) {
panic(fmt.Sprintf("lockedfile.File %s became unreachable without a call to Close", f.Name()))
})
return f, nil
}
// Open is like os.Open, but returns a read-locked file.
func Open(name string) (*File, error) {
return OpenFile(name, os.O_RDONLY, 0)
}
// Create is like os.Create, but returns a write-locked file.
func Create(name string) (*File, error) {
return OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
}
// Edit creates the named file with mode 0666 (before umask),
// but does not truncate existing contents.
//
// If Edit succeeds, methods on the returned File can be used for I/O.
// The associated file descriptor has mode O_RDWR and the file is write-locked.
func Edit(name string) (*File, error) {
return OpenFile(name, os.O_RDWR|os.O_CREATE, 0666)
}
// Close unlocks and closes the underlying file.
//
// Close may be called multiple times; all calls after the first will return a
// non-nil error.
func (f *File) Close() error {
if f.closed {
return &os.PathError{
Op: "close",
Path: f.Name(),
Err: os.ErrClosed,
}
}
f.closed = true
err := closeFile(f.osFile.File)
runtime.SetFinalizer(f, nil)
return err
}
// Read opens the named file with a read-lock and returns its contents.
func Read(name string) ([]byte, error) {
f, err := Open(name)
if err != nil {
return nil, err
}
defer f.Close()
return ioutil.ReadAll(f)
}
// Write opens the named file (creating it with the given permissions if needed),
// then write-locks it and overwrites it with the given content.
func Write(name string, content io.Reader, perm os.FileMode) (err error) {
f, err := OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
if err != nil {
return err
}
_, err = io.Copy(f, content)
if closeErr := f.Close(); err == nil {
err = closeErr
}
return err
}

View File

@ -0,0 +1,64 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !plan9
package lockedfile
import (
"os"
"cmd/go/internal/lockedfile/internal/filelock"
)
func openFile(name string, flag int, perm os.FileMode) (*os.File, error) {
// On BSD systems, we could add the O_SHLOCK or O_EXLOCK flag to the OpenFile
// call instead of locking separately, but we have to support separate locking
// calls for Linux and Windows anyway, so it's simpler to use that approach
// consistently.
f, err := os.OpenFile(name, flag&^os.O_TRUNC, perm)
if err != nil {
return nil, err
}
switch flag & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR) {
case os.O_WRONLY, os.O_RDWR:
err = filelock.Lock(f)
default:
err = filelock.RLock(f)
}
if err != nil {
f.Close()
return nil, err
}
if flag&os.O_TRUNC == os.O_TRUNC {
if err := f.Truncate(0); err != nil {
// The documentation for os.O_TRUNC says “if possible, truncate file when
// opened”, but doesn't define “possible” (golang.org/issue/28699).
// We'll treat regular files (and symlinks to regular files) as “possible”
// and ignore errors for the rest.
if fi, statErr := f.Stat(); statErr != nil || fi.Mode().IsRegular() {
filelock.Unlock(f)
f.Close()
return nil, err
}
}
}
return f, nil
}
func closeFile(f *os.File) error {
// Since locking syscalls operate on file descriptors, we must unlock the file
// while the descriptor is still valid — that is, before the file is closed —
// and avoid unlocking files that are already closed.
err := filelock.Unlock(f)
if closeErr := f.Close(); err == nil {
err = closeErr
}
return err
}

View File

@ -0,0 +1,93 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build plan9
package lockedfile
import (
"math/rand"
"os"
"strings"
"time"
)
// Opening an exclusive-use file returns an error.
// The expected error strings are:
//
// - "open/create -- file is locked" (cwfs, kfs)
// - "exclusive lock" (fossil)
// - "exclusive use file already open" (ramfs)
var lockedErrStrings = [...]string{
"file is locked",
"exclusive lock",
"exclusive use file already open",
}
// Even though plan9 doesn't support the Lock/RLock/Unlock functions to
// manipulate already-open files, IsLocked is still meaningful: os.OpenFile
// itself may return errors that indicate that a file with the ModeExclusive bit
// set is already open.
func isLocked(err error) bool {
s := err.Error()
for _, frag := range lockedErrStrings {
if strings.Contains(s, frag) {
return true
}
}
return false
}
func openFile(name string, flag int, perm os.FileMode) (*os.File, error) {
// Plan 9 uses a mode bit instead of explicit lock/unlock syscalls.
//
// Per http://man.cat-v.org/plan_9/5/stat: “Exclusive use files may be open
// for I/O by only one fid at a time across all clients of the server. If a
// second open is attempted, it draws an error.”
//
// So we can try to open a locked file, but if it fails we're on our own to
// figure out when it becomes available. We'll use exponential backoff with
// some jitter and an arbitrary limit of 500ms.
// If the file was unpacked or created by some other program, it might not
// have the ModeExclusive bit set. Set it before we call OpenFile, so that we
// can be confident that a successful OpenFile implies exclusive use.
if fi, err := os.Stat(name); err == nil {
if fi.Mode()&os.ModeExclusive == 0 {
if err := os.Chmod(name, fi.Mode()|os.ModeExclusive); err != nil {
return nil, err
}
}
} else if !os.IsNotExist(err) {
return nil, err
}
nextSleep := 1 * time.Millisecond
const maxSleep = 500 * time.Millisecond
for {
f, err := os.OpenFile(name, flag, perm|os.ModeExclusive)
if err == nil {
return f, nil
}
if !isLocked(err) {
return nil, err
}
time.Sleep(nextSleep)
nextSleep += nextSleep
if nextSleep > maxSleep {
nextSleep = maxSleep
}
// Apply 10% jitter to avoid synchronizing collisions.
nextSleep += time.Duration((0.1*rand.Float64() - 0.05) * float64(nextSleep))
}
}
func closeFile(f *os.File) error {
return f.Close()
}

View File

@ -0,0 +1,174 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// js and nacl do not support inter-process file locking.
// +build !js,!nacl
package lockedfile_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"time"
"cmd/go/internal/lockedfile"
)
func mustTempDir(t *testing.T) (dir string, remove func()) {
t.Helper()
dir, err := ioutil.TempDir("", filepath.Base(t.Name()))
if err != nil {
t.Fatal(err)
}
return dir, func() { os.RemoveAll(dir) }
}
const (
quiescent = 10 * time.Millisecond
probablyStillBlocked = 10 * time.Second
)
func mustBlock(t *testing.T, desc string, f func()) (wait func(*testing.T)) {
t.Helper()
done := make(chan struct{})
go func() {
f()
close(done)
}()
select {
case <-done:
t.Fatalf("%s unexpectedly did not block", desc)
return nil
case <-time.After(quiescent):
return func(t *testing.T) {
t.Helper()
select {
case <-time.After(probablyStillBlocked):
t.Fatalf("%s is unexpectedly still blocked after %v", desc, probablyStillBlocked)
case <-done:
}
}
}
}
func TestMutexExcludes(t *testing.T) {
t.Parallel()
dir, remove := mustTempDir(t)
defer remove()
path := filepath.Join(dir, "lock")
mu := lockedfile.MutexAt(path)
t.Logf("mu := MutexAt(_)")
unlock, err := mu.Lock()
if err != nil {
t.Fatalf("mu.Lock: %v", err)
}
t.Logf("unlock, _ := mu.Lock()")
mu2 := lockedfile.MutexAt(mu.Path)
t.Logf("mu2 := MutexAt(mu.Path)")
wait := mustBlock(t, "mu2.Lock()", func() {
unlock2, err := mu2.Lock()
if err != nil {
t.Errorf("mu2.Lock: %v", err)
return
}
t.Logf("unlock2, _ := mu2.Lock()")
t.Logf("unlock2()")
unlock2()
})
t.Logf("unlock()")
unlock()
wait(t)
}
func TestReadWaitsForLock(t *testing.T) {
t.Parallel()
dir, remove := mustTempDir(t)
defer remove()
path := filepath.Join(dir, "timestamp.txt")
f, err := lockedfile.Create(path)
if err != nil {
t.Fatalf("Create: %v", err)
}
defer f.Close()
const (
part1 = "part 1\n"
part2 = "part 2\n"
)
_, err = f.WriteString(part1)
if err != nil {
t.Fatalf("WriteString: %v", err)
}
t.Logf("WriteString(%q) = <nil>", part1)
wait := mustBlock(t, "Read", func() {
b, err := lockedfile.Read(path)
if err != nil {
t.Errorf("Read: %v", err)
return
}
const want = part1 + part2
got := string(b)
if got == want {
t.Logf("Read(_) = %q", got)
} else {
t.Errorf("Read(_) = %q, _; want %q", got, want)
}
})
_, err = f.WriteString(part2)
if err != nil {
t.Errorf("WriteString: %v", err)
} else {
t.Logf("WriteString(%q) = <nil>", part2)
}
f.Close()
wait(t)
}
func TestCanLockExistingFile(t *testing.T) {
t.Parallel()
dir, remove := mustTempDir(t)
defer remove()
path := filepath.Join(dir, "existing.txt")
if err := ioutil.WriteFile(path, []byte("ok"), 0777); err != nil {
t.Fatalf("ioutil.WriteFile: %v", err)
}
f, err := lockedfile.Edit(path)
if err != nil {
t.Fatalf("first Edit: %v", err)
}
wait := mustBlock(t, "Edit", func() {
other, err := lockedfile.Edit(path)
if err != nil {
t.Errorf("second Edit: %v", err)
}
other.Close()
})
f.Close()
wait(t)
}

View File

@ -0,0 +1,60 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package lockedfile
import (
"fmt"
"os"
)
// A Mutex provides mutual exclusion within and across processes by locking a
// well-known file. Such a file generally guards some other part of the
// filesystem: for example, a Mutex file in a directory might guard access to
// the entire tree rooted in that directory.
//
// Mutex does not implement sync.Locker: unlike a sync.Mutex, a lockedfile.Mutex
// can fail to lock (e.g. if there is a permission error in the filesystem).
//
// Like a sync.Mutex, a Mutex may be included as a field of a larger struct but
// must not be copied after first use. The Path field must be set before first
// use and must not be change thereafter.
type Mutex struct {
Path string // The path to the well-known lock file. Must be non-empty.
}
// MutexAt returns a new Mutex with Path set to the given non-empty path.
func MutexAt(path string) *Mutex {
if path == "" {
panic("lockedfile.MutexAt: path must be non-empty")
}
return &Mutex{Path: path}
}
func (mu *Mutex) String() string {
return fmt.Sprintf("lockedfile.Mutex(%s)", mu.Path)
}
// Lock attempts to lock the Mutex.
//
// If successful, Lock returns a non-nil unlock function: it is provided as a
// return-value instead of a separate method to remind the caller to check the
// accompanying error. (See https://golang.org/issue/20803.)
func (mu *Mutex) Lock() (unlock func(), err error) {
if mu.Path == "" {
panic("lockedfile.Mutex: missing Path during Lock")
}
// We could use either O_RDWR or O_WRONLY here. If we choose O_RDWR and the
// file at mu.Path is write-only, the call to OpenFile will fail with a
// permission error. That's actually what we want: if we add an RLock method
// in the future, it should call OpenFile with O_RDONLY and will require the
// files must be readable, so we should not let the caller make any
// assumptions about Mutex working with write-only files.
f, err := OpenFile(mu.Path, os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
return nil, err
}
return func() { f.Close() }, nil
}

View File

@ -15,7 +15,7 @@ import (
)
var cmdDownload = &base.Command{
UsageLine: "go mod download [-dir] [-json] [modules]",
UsageLine: "go mod download [-json] [modules]",
Short: "download modules to local cache",
Long: `
Download downloads the named modules, which can be module patterns selecting
@ -128,6 +128,16 @@ func runDownload(cmd *base.Command, args []string) {
base.Fatalf("%v", err)
}
os.Stdout.Write(append(b, '\n'))
if m.Error != "" {
base.SetExitStatus(1)
}
}
} else {
for _, m := range mods {
if m.Error != "" {
base.Errorf("%s@%s: %s\n", m.Path, m.Version, m.Error)
}
}
base.ExitIfErrors()
}
}

View File

@ -7,6 +7,7 @@
package modcmd
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
@ -15,6 +16,7 @@ import (
"strings"
"cmd/go/internal/base"
"cmd/go/internal/modfetch"
"cmd/go/internal/modfile"
"cmd/go/internal/modload"
"cmd/go/internal/module"
@ -62,6 +64,8 @@ The -require, -droprequire, -exclude, -dropexclude, -replace,
and -dropreplace editing flags may be repeated, and the changes
are applied in the order given.
The -go=version flag sets the expected Go language version.
The -print flag prints the final go.mod in its text format instead of
writing it back to go.mod.
@ -74,7 +78,8 @@ writing it back to go.mod. The JSON output corresponds to these Go types:
}
type GoMod struct {
Module Module
Module Module
Go string
Require []Require
Exclude []Module
Replace []Replace
@ -102,8 +107,8 @@ by invoking 'go mod edit' with -require, -exclude, and so on.
}
var (
editFmt = cmdEdit.Flag.Bool("fmt", false, "")
// editGo = cmdEdit.Flag.String("go", "", "")
editFmt = cmdEdit.Flag.Bool("fmt", false, "")
editGo = cmdEdit.Flag.String("go", "", "")
editJSON = cmdEdit.Flag.Bool("json", false, "")
editPrint = cmdEdit.Flag.Bool("print", false, "")
editModule = cmdEdit.Flag.String("module", "", "")
@ -131,6 +136,7 @@ func init() {
func runEdit(cmd *base.Command, args []string) {
anyFlags :=
*editModule != "" ||
*editGo != "" ||
*editJSON ||
*editPrint ||
*editFmt ||
@ -151,8 +157,7 @@ func runEdit(cmd *base.Command, args []string) {
if len(args) == 1 {
gomod = args[0]
} else {
modload.MustInit()
gomod = filepath.Join(modload.ModRoot, "go.mod")
gomod = filepath.Join(modload.ModRoot(), "go.mod")
}
if *editModule != "" {
@ -161,7 +166,11 @@ func runEdit(cmd *base.Command, args []string) {
}
}
// TODO(rsc): Implement -go= once we start advertising it.
if *editGo != "" {
if !modfile.GoVersionRE.MatchString(*editGo) {
base.Fatalf(`go mod: invalid -go option; expecting something like "-go 1.12"`)
}
}
data, err := ioutil.ReadFile(gomod)
if err != nil {
@ -174,7 +183,13 @@ func runEdit(cmd *base.Command, args []string) {
}
if *editModule != "" {
modFile.AddModuleStmt(modload.CmdModModule)
modFile.AddModuleStmt(*editModule)
}
if *editGo != "" {
if err := modFile.AddGoStmt(*editGo); err != nil {
base.Fatalf("go: internal error: %v", err)
}
}
if len(edits) > 0 {
@ -190,17 +205,23 @@ func runEdit(cmd *base.Command, args []string) {
return
}
data, err = modFile.Format()
out, err := modFile.Format()
if err != nil {
base.Fatalf("go: %v", err)
}
if *editPrint {
os.Stdout.Write(data)
os.Stdout.Write(out)
return
}
if err := ioutil.WriteFile(gomod, data, 0666); err != nil {
unlock := modfetch.SideLock()
defer unlock()
lockedData, err := ioutil.ReadFile(gomod)
if err == nil && !bytes.Equal(lockedData, data) {
base.Fatalf("go: go.mod changed during editing; not overwriting")
}
if err := ioutil.WriteFile(gomod, out, 0666); err != nil {
base.Fatalf("go: %v", err)
}
}
@ -344,6 +365,7 @@ func flagDropReplace(arg string) {
// fileJSON is the -json output data structure.
type fileJSON struct {
Module module.Version
Go string `json:",omitempty"`
Require []requireJSON
Exclude []module.Version
Replace []replaceJSON
@ -364,6 +386,9 @@ type replaceJSON struct {
func editPrintJSON(modFile *modfile.File) {
var f fileJSON
f.Module = modFile.Module.Mod
if modFile.Go != nil {
f.Go = modFile.Go.Version
}
for _, r := range modFile.Require {
f.Require = append(f.Require, requireJSON{Path: r.Mod.Path, Version: r.Mod.Version, Indirect: r.Indirect})
}

View File

@ -10,6 +10,7 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/modload"
"os"
"strings"
)
var cmdInit = &base.Command{
@ -37,5 +38,8 @@ func runInit(cmd *base.Command, args []string) {
if _, err := os.Stat("go.mod"); err == nil {
base.Fatalf("go mod init: go.mod already exists")
}
if strings.Contains(modload.CmdModModule, "@") {
base.Fatalf("go mod init: module path must not contain '@'")
}
modload.InitMod() // does all the hard work
}

View File

@ -77,7 +77,17 @@ func modTidyGoSum() {
keep := make(map[module.Version]bool)
var walk func(module.Version)
walk = func(m module.Version) {
keep[m] = true
// If we build using a replacement module, keep the sum for the replacement,
// since that's the code we'll actually use during a build.
//
// TODO(golang.org/issue/29182): Perhaps we should keep both sums, and the
// sums for both sets of transitive requirements.
r := modload.Replacement(m)
if r.Path == "" {
keep[m] = true
} else {
keep[r] = true
}
list, _ := reqs.Required(m)
for _, r := range list {
if !keep[r] {

View File

@ -43,9 +43,9 @@ func runVendor(cmd *base.Command, args []string) {
}
pkgs := modload.LoadVendor()
vdir := filepath.Join(modload.ModRoot, "vendor")
vdir := filepath.Join(modload.ModRoot(), "vendor")
if err := os.RemoveAll(vdir); err != nil {
base.Fatalf("go vendor: %v", err)
base.Fatalf("go mod vendor: %v", err)
}
modpkgs := make(map[module.Version][]string)
@ -85,7 +85,7 @@ func runVendor(cmd *base.Command, args []string) {
return
}
if err := ioutil.WriteFile(filepath.Join(vdir, "modules.txt"), buf.Bytes(), 0666); err != nil {
base.Fatalf("go vendor: %v", err)
base.Fatalf("go mod vendor: %v", err)
}
}
@ -172,10 +172,10 @@ func matchNonTest(info os.FileInfo) bool {
func copyDir(dst, src string, match func(os.FileInfo) bool) {
files, err := ioutil.ReadDir(src)
if err != nil {
base.Fatalf("go vendor: %v", err)
base.Fatalf("go mod vendor: %v", err)
}
if err := os.MkdirAll(dst, 0777); err != nil {
base.Fatalf("go vendor: %v", err)
base.Fatalf("go mod vendor: %v", err)
}
for _, file := range files {
if file.IsDir() || !file.Mode().IsRegular() || !match(file) {
@ -183,18 +183,18 @@ func copyDir(dst, src string, match func(os.FileInfo) bool) {
}
r, err := os.Open(filepath.Join(src, file.Name()))
if err != nil {
base.Fatalf("go vendor: %v", err)
base.Fatalf("go mod vendor: %v", err)
}
w, err := os.Create(filepath.Join(dst, file.Name()))
if err != nil {
base.Fatalf("go vendor: %v", err)
base.Fatalf("go mod vendor: %v", err)
}
if _, err := io.Copy(w, r); err != nil {
base.Fatalf("go vendor: %v", err)
base.Fatalf("go mod vendor: %v", err)
}
r.Close()
if err := w.Close(); err != nil {
base.Fatalf("go vendor: %v", err)
base.Fatalf("go mod vendor: %v", err)
}
}
}

View File

@ -146,7 +146,7 @@ func TestConvertLegacyConfig(t *testing.T) {
}
for _, tt := range tests {
t.Run(strings.Replace(tt.path, "/", "_", -1)+"_"+tt.vers, func(t *testing.T) {
t.Run(strings.ReplaceAll(tt.path, "/", "_")+"_"+tt.vers, func(t *testing.T) {
f, err := modfile.Parse("golden", []byte(tt.gomod), nil)
if err != nil {
t.Fatal(err)

View File

@ -8,15 +8,18 @@ import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"cmd/go/internal/base"
"cmd/go/internal/lockedfile"
"cmd/go/internal/modfetch/codehost"
"cmd/go/internal/module"
"cmd/go/internal/par"
"cmd/go/internal/renameio"
"cmd/go/internal/semver"
)
@ -53,6 +56,8 @@ func CachePath(m module.Version, suffix string) (string, error) {
return filepath.Join(dir, encVer+"."+suffix), nil
}
// DownloadDir returns the directory to which m should be downloaded.
// Note that the directory may not yet exist.
func DownloadDir(m module.Version) (string, error) {
if PkgMod == "" {
return "", fmt.Errorf("internal error: modfetch.PkgMod not set")
@ -74,6 +79,37 @@ func DownloadDir(m module.Version) (string, error) {
return filepath.Join(PkgMod, enc+"@"+encVer), nil
}
// lockVersion locks a file within the module cache that guards the downloading
// and extraction of the zipfile for the given module version.
func lockVersion(mod module.Version) (unlock func(), err error) {
path, err := CachePath(mod, "lock")
if err != nil {
return nil, err
}
if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
return nil, err
}
return lockedfile.MutexAt(path).Lock()
}
// SideLock locks a file within the module cache that that guards edits to files
// outside the cache, such as go.sum and go.mod files in the user's working
// directory. It returns a function that must be called to unlock the file.
func SideLock() (unlock func()) {
if PkgMod == "" {
base.Fatalf("go: internal error: modfetch.PkgMod not set")
}
path := filepath.Join(PkgMod, "cache", "lock")
if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
base.Fatalf("go: failed to create cache directory %s: %v", filepath.Dir(path), err)
}
unlock, err := lockedfile.MutexAt(path).Lock()
if err != nil {
base.Fatalf("go: failed to lock file at %v", path)
}
return unlock
}
// A cachingRepo is a cache around an underlying Repo,
// avoiding redundant calls to ModulePath, Versions, Stat, Latest, and GoMod (but not Zip).
// It is also safe for simultaneous use by multiple goroutines
@ -129,16 +165,18 @@ func (r *cachingRepo) Stat(rev string) (*RevInfo, error) {
}
info, err = r.r.Stat(rev)
if err == nil {
if err := writeDiskStat(file, info); err != nil {
fmt.Fprintf(os.Stderr, "go: writing stat cache: %v\n", err)
}
// If we resolved, say, 1234abcde to v0.0.0-20180604122334-1234abcdef78,
// then save the information under the proper version, for future use.
if info.Version != rev {
file, _ = CachePath(module.Version{Path: r.path, Version: info.Version}, "info")
r.cache.Do("stat:"+info.Version, func() interface{} {
return cachedInfo{info, err}
})
}
if err := writeDiskStat(file, info); err != nil {
fmt.Fprintf(os.Stderr, "go: writing stat cache: %v\n", err)
}
}
return cachedInfo{info, err}
}).(cachedInfo)
@ -213,8 +251,8 @@ func (r *cachingRepo) GoMod(rev string) ([]byte, error) {
return append([]byte(nil), c.text...), nil
}
func (r *cachingRepo) Zip(version, tmpdir string) (string, error) {
return r.r.Zip(version, tmpdir)
func (r *cachingRepo) Zip(dst io.Writer, version string) error {
return r.r.Zip(dst, version)
}
// Stat is like Lookup(path).Stat(rev) but avoids the
@ -383,7 +421,7 @@ func readDiskStatByHash(path, rev string) (file string, info *RevInfo, err error
// and should ignore it.
var oldVgoPrefix = []byte("//vgo 0.0.")
// readDiskGoMod reads a cached stat result from disk,
// readDiskGoMod reads a cached go.mod file from disk,
// returning the name of the cache file and the result.
// If the read fails, the caller can use
// writeDiskGoMod(file, data) to write a new cache entry.
@ -449,22 +487,8 @@ func writeDiskCache(file string, data []byte) error {
if err := os.MkdirAll(filepath.Dir(file), 0777); err != nil {
return err
}
// Write data to temp file next to target file.
f, err := ioutil.TempFile(filepath.Dir(file), filepath.Base(file)+".tmp-")
if err != nil {
return err
}
defer os.Remove(f.Name())
defer f.Close()
if _, err := f.Write(data); err != nil {
return err
}
if err := f.Close(); err != nil {
return err
}
// Rename temp file onto cache file,
// so that the cache file is always a complete file.
if err := os.Rename(f.Name(), file); err != nil {
if err := renameio.WriteFile(file, data); err != nil {
return err
}
@ -481,8 +505,18 @@ func rewriteVersionList(dir string) {
base.Fatalf("go: internal error: misuse of rewriteVersionList")
}
// TODO(rsc): We should do some kind of directory locking here,
// to avoid lost updates.
listFile := filepath.Join(dir, "list")
// We use a separate lockfile here instead of locking listFile itself because
// we want to use Rename to write the file atomically. The list may be read by
// a GOPROXY HTTP server, and if we crash midway through a rewrite (or if the
// HTTP server ignores our locking and serves the file midway through a
// rewrite) it's better to serve a stale list than a truncated one.
unlock, err := lockedfile.MutexAt(listFile + ".lock").Lock()
if err != nil {
base.Fatalf("go: can't lock version list lockfile: %v", err)
}
defer unlock()
infos, err := ioutil.ReadDir(dir)
if err != nil {
@ -511,12 +545,12 @@ func rewriteVersionList(dir string) {
buf.WriteString(v)
buf.WriteString("\n")
}
listFile := filepath.Join(dir, "list")
old, _ := ioutil.ReadFile(listFile)
if bytes.Equal(buf.Bytes(), old) {
return
}
// TODO: Use rename to install file,
// so that readers never see an incomplete file.
ioutil.WriteFile(listFile, buf.Bytes(), 0666)
if err := renameio.WriteFile(listFile, buf.Bytes()); err != nil {
base.Fatalf("go: failed to write version list: %v", err)
}
}

View File

@ -20,6 +20,7 @@ import (
"time"
"cmd/go/internal/cfg"
"cmd/go/internal/lockedfile"
"cmd/go/internal/str"
)
@ -131,9 +132,9 @@ 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) (string, error) {
func WorkDir(typ, name string) (dir, lockfile string, err error) {
if WorkRoot == "" {
return "", fmt.Errorf("codehost.WorkRoot not set")
return "", "", fmt.Errorf("codehost.WorkRoot not set")
}
// We name the work directory for the SHA256 hash of the type and name.
@ -142,22 +143,41 @@ func WorkDir(typ, name string) (string, error) {
// that one checkout is never nested inside another. That nesting has
// led to security problems in the past.
if strings.Contains(typ, ":") {
return "", fmt.Errorf("codehost.WorkDir: type cannot contain colon")
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(WorkRoot, 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)
}
if err := os.MkdirAll(filepath.Dir(dir), 0777); err != nil {
return "", "", err
}
lockfile = dir + ".lock"
if cfg.BuildX {
fmt.Fprintf(os.Stderr, "# lock %s", lockfile)
}
unlock, err := lockedfile.MutexAt(lockfile).Lock()
if err != nil {
return "", "", fmt.Errorf("codehost.WorkDir: can't find or create lock file: %v", err)
}
defer unlock()
data, err := ioutil.ReadFile(dir + ".info")
info, err2 := os.Stat(dir)
if err == nil && err2 == nil && info.IsDir() {
// Info file and directory both already exist: reuse.
have := strings.TrimSuffix(string(data), "\n")
if have != key {
return "", fmt.Errorf("%s exists with wrong content (have %q want %q)", dir+".info", have, key)
return "", "", fmt.Errorf("%s exists with wrong content (have %q want %q)", dir+".info", have, key)
}
if cfg.BuildX {
fmt.Fprintf(os.Stderr, "# %s for %s %s\n", dir, typ, name)
}
return dir, nil
return dir, lockfile, nil
}
// Info file or directory missing. Start from scratch.
@ -166,26 +186,30 @@ func WorkDir(typ, name string) (string, error) {
}
os.RemoveAll(dir)
if err := os.MkdirAll(dir, 0777); err != nil {
return "", err
return "", "", err
}
if err := ioutil.WriteFile(dir+".info", []byte(key), 0666); err != nil {
os.RemoveAll(dir)
return "", err
return "", "", err
}
return dir, nil
return dir, lockfile, nil
}
type RunError struct {
Cmd string
Err error
Stderr []byte
Cmd string
Err error
Stderr []byte
HelpText string
}
func (e *RunError) Error() string {
text := e.Cmd + ": " + e.Err.Error()
stderr := bytes.TrimRight(e.Stderr, "\n")
if len(stderr) > 0 {
text += ":\n\t" + strings.Replace(string(stderr), "\n", "\n\t", -1)
text += ":\n\t" + strings.ReplaceAll(string(stderr), "\n", "\n\t")
}
if len(e.HelpText) > 0 {
text += "\n" + e.HelpText
}
return text
}

View File

@ -17,6 +17,7 @@ import (
"sync"
"time"
"cmd/go/internal/lockedfile"
"cmd/go/internal/par"
)
@ -57,22 +58,29 @@ func newGitRepo(remote string, localOK bool) (Repo, error) {
r := &gitRepo{remote: remote}
if strings.Contains(remote, "://") {
// This is a remote path.
dir, err := WorkDir(gitWorkDirType, r.remote)
var err error
r.dir, r.mu.Path, err = WorkDir(gitWorkDirType, r.remote)
if err != nil {
return nil, err
}
r.dir = dir
if _, err := os.Stat(filepath.Join(dir, "objects")); err != nil {
if _, err := Run(dir, "git", "init", "--bare"); err != nil {
os.RemoveAll(dir)
unlock, err := r.mu.Lock()
if err != nil {
return nil, err
}
defer unlock()
if _, err := os.Stat(filepath.Join(r.dir, "objects")); err != nil {
if _, err := Run(r.dir, "git", "init", "--bare"); err != nil {
os.RemoveAll(r.dir)
return nil, err
}
// We could just say git fetch https://whatever later,
// but this lets us say git fetch origin instead, which
// is a little nicer. More importantly, using a named remote
// avoids a problem with Git LFS. See golang.org/issue/25605.
if _, err := Run(dir, "git", "remote", "add", "origin", r.remote); err != nil {
os.RemoveAll(dir)
if _, err := Run(r.dir, "git", "remote", "add", "origin", r.remote); err != nil {
os.RemoveAll(r.dir)
return nil, err
}
r.remote = "origin"
@ -97,6 +105,7 @@ func newGitRepo(remote string, localOK bool) (Repo, error) {
return nil, fmt.Errorf("%s exists but is not a directory", remote)
}
r.dir = remote
r.mu.Path = r.dir + ".lock"
}
return r, nil
}
@ -106,7 +115,8 @@ type gitRepo struct {
local bool
dir string
mu sync.Mutex // protects fetchLevel, some git repo state
mu lockedfile.Mutex // protects fetchLevel and git repo state
fetchLevel int
statCache par.Cache
@ -154,6 +164,11 @@ func (r *gitRepo) loadRefs() {
// Most of the time we only care about tags but sometimes we care about heads too.
out, err := Run(r.dir, "git", "ls-remote", "-q", r.remote)
if err != nil {
if rerr, ok := err.(*RunError); ok {
if bytes.Contains(rerr.Stderr, []byte("fatal: could not read Username")) {
rerr.HelpText = "If this is a private repository, see https://golang.org/doc/faq#git_https for additional information."
}
}
r.refsErr = err
return
}
@ -304,11 +319,11 @@ func (r *gitRepo) stat(rev string) (*RevInfo, error) {
}
// Protect r.fetchLevel and the "fetch more and more" sequence.
// TODO(rsc): Add LockDir and use it for protecting that
// sequence, so that multiple processes don't collide in their
// git commands.
r.mu.Lock()
defer r.mu.Unlock()
unlock, err := r.mu.Lock()
if err != nil {
return nil, err
}
defer unlock()
// Perhaps r.localTags did not have the ref when we loaded local tags,
// but we've since done fetches that pulled down the hash we need
@ -495,8 +510,11 @@ func (r *gitRepo) ReadFileRevs(revs []string, file string, maxSize int64) (map[s
// Protect r.fetchLevel and the "fetch more and more" sequence.
// See stat method above.
r.mu.Lock()
defer r.mu.Unlock()
unlock, err := r.mu.Lock()
if err != nil {
return nil, err
}
defer unlock()
var refs []string
var protoFlag []string
@ -658,8 +676,11 @@ func (r *gitRepo) RecentTag(rev, prefix string) (tag string, err error) {
// There are plausible tags, but we don't know if rev is a descendent of any of them.
// Fetch the history to find out.
r.mu.Lock()
defer r.mu.Unlock()
unlock, err := r.mu.Lock()
if err != nil {
return "", err
}
defer unlock()
if r.fetchLevel < fetchAll {
// Fetch all heads and tags and see if that gives us enough history.
@ -678,7 +699,7 @@ func (r *gitRepo) RecentTag(rev, prefix string) (tag string, err error) {
// unreachable for a reason).
//
// Try one last time in case some other goroutine fetched rev while we were
// waiting on r.mu.
// waiting on the lock.
describe()
return tag, err
}
@ -694,6 +715,16 @@ func (r *gitRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser,
return nil, "", err
}
unlock, err := r.mu.Lock()
if err != nil {
return nil, "", err
}
defer unlock()
if err := ensureGitAttributes(r.dir); err != nil {
return nil, "", err
}
// Incredibly, git produces different archives depending on whether
// it is running on a Windows system or not, in an attempt to normalize
// text file line endings. Setting -c core.autocrlf=input means only
@ -709,3 +740,43 @@ func (r *gitRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser,
return ioutil.NopCloser(bytes.NewReader(archive)), "", nil
}
// ensureGitAttributes makes sure export-subst and export-ignore features are
// disabled for this repo. This is intended to be run prior to running git
// archive so that zip files are generated that produce consistent ziphashes
// for a given revision, independent of variables such as git version and the
// size of the repo.
//
// See: https://github.com/golang/go/issues/27153
func ensureGitAttributes(repoDir string) (err error) {
const attr = "\n* -export-subst -export-ignore\n"
d := repoDir + "/info"
p := d + "/attributes"
if err := os.MkdirAll(d, 0755); err != nil {
return err
}
f, err := os.OpenFile(p, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
if err != nil {
return err
}
defer func() {
closeErr := f.Close()
if closeErr != nil {
err = closeErr
}
}()
b, err := ioutil.ReadAll(f)
if err != nil {
return err
}
if !bytes.HasSuffix(b, []byte(attr)) {
_, err := f.WriteString(attr)
return err
}
return nil
}

View File

@ -18,6 +18,7 @@ import (
"sync"
"time"
"cmd/go/internal/lockedfile"
"cmd/go/internal/par"
"cmd/go/internal/str"
)
@ -27,12 +28,19 @@ import (
// to get the code, but we can't access it due to the error.
// The caller should report this error instead of continuing to probe
// other possible module paths.
//
// TODO(bcmills): See if we can invert this. (Return a distinguished error for
// “repo not found” and treat everything else as terminal.)
type VCSError struct {
Err error
}
func (e *VCSError) Error() string { return e.Err.Error() }
func vcsErrorf(format string, a ...interface{}) error {
return &VCSError{Err: fmt.Errorf(format, a...)}
}
func NewRepo(vcs, remote string) (Repo, error) {
type key struct {
vcs string
@ -56,6 +64,8 @@ func NewRepo(vcs, remote string) (Repo, error) {
var vcsRepoCache par.Cache
type vcsRepo struct {
mu lockedfile.Mutex // protects all commands, so we don't have to decide which are safe on a per-VCS basis
remote string
cmd *vcsCmd
dir string
@ -81,18 +91,27 @@ func newVCSRepo(vcs, remote string) (Repo, error) {
if !strings.Contains(remote, "://") {
return nil, fmt.Errorf("invalid vcs remote: %s %s", vcs, remote)
}
r := &vcsRepo{remote: remote, cmd: cmd}
if cmd.init == nil {
return r, nil
}
dir, err := WorkDir(vcsWorkDirType+vcs, r.remote)
var err error
r.dir, r.mu.Path, err = WorkDir(vcsWorkDirType+vcs, r.remote)
if err != nil {
return nil, err
}
r.dir = dir
if _, err := os.Stat(filepath.Join(dir, "."+vcs)); err != nil {
if _, err := Run(dir, cmd.init(r.remote)); err != nil {
os.RemoveAll(dir)
if cmd.init == nil {
return r, nil
}
unlock, err := r.mu.Lock()
if err != nil {
return nil, err
}
defer unlock()
if _, err := os.Stat(filepath.Join(r.dir, "."+vcs)); err != nil {
if _, err := Run(r.dir, cmd.init(r.remote)); err != nil {
os.RemoveAll(r.dir)
return nil, err
}
}
@ -270,6 +289,12 @@ func (r *vcsRepo) loadBranches() {
}
func (r *vcsRepo) Tags(prefix string) ([]string, error) {
unlock, err := r.mu.Lock()
if err != nil {
return nil, err
}
defer unlock()
r.tagsOnce.Do(r.loadTags)
tags := []string{}
@ -283,6 +308,12 @@ func (r *vcsRepo) Tags(prefix string) ([]string, error) {
}
func (r *vcsRepo) Stat(rev string) (*RevInfo, error) {
unlock, err := r.mu.Lock()
if err != nil {
return nil, err
}
defer unlock()
if rev == "latest" {
rev = r.cmd.latest
}
@ -315,7 +346,7 @@ func (r *vcsRepo) fetch() {
func (r *vcsRepo) statLocal(rev string) (*RevInfo, error) {
out, err := Run(r.dir, r.cmd.statLocal(rev, r.remote))
if err != nil {
return nil, fmt.Errorf("unknown revision %s", rev)
return nil, vcsErrorf("unknown revision %s", rev)
}
return r.cmd.parseStat(rev, string(out))
}
@ -332,6 +363,14 @@ func (r *vcsRepo) ReadFile(rev, file string, maxSize int64) ([]byte, error) {
if err != nil {
return nil, err
}
// r.Stat acquires r.mu, so lock after that.
unlock, err := r.mu.Lock()
if err != nil {
return nil, err
}
defer unlock()
out, err := Run(r.dir, r.cmd.readFile(rev, file, r.remote))
if err != nil {
return nil, os.ErrNotExist
@ -340,14 +379,42 @@ func (r *vcsRepo) ReadFile(rev, file string, maxSize int64) ([]byte, error) {
}
func (r *vcsRepo) ReadFileRevs(revs []string, file string, maxSize int64) (map[string]*FileRev, error) {
return nil, fmt.Errorf("ReadFileRevs not implemented")
// We don't technically need to lock here since we're returning an error
// uncondititonally, but doing so anyway will help to avoid baking in
// lock-inversion bugs.
unlock, err := r.mu.Lock()
if err != nil {
return nil, err
}
defer unlock()
return nil, vcsErrorf("ReadFileRevs not implemented")
}
func (r *vcsRepo) RecentTag(rev, prefix string) (tag string, err error) {
return "", fmt.Errorf("RecentTags not implemented")
// We don't technically need to lock here since we're returning an error
// uncondititonally, but doing so anyway will help to avoid baking in
// lock-inversion bugs.
unlock, err := r.mu.Lock()
if err != nil {
return "", err
}
defer unlock()
return "", vcsErrorf("RecentTag not implemented")
}
func (r *vcsRepo) ReadZip(rev, subdir string, maxSize int64) (zip io.ReadCloser, actualSubdir string, err error) {
if r.cmd.readZip == nil {
return nil, "", vcsErrorf("ReadZip not implemented for %s", r.cmd.vcs)
}
unlock, err := r.mu.Lock()
if err != nil {
return nil, "", err
}
defer unlock()
if rev == "latest" {
rev = r.cmd.latest
}
@ -392,7 +459,7 @@ func (d *deleteCloser) Close() error {
func hgParseStat(rev, out string) (*RevInfo, error) {
f := strings.Fields(string(out))
if len(f) < 3 {
return nil, fmt.Errorf("unexpected response from hg log: %q", out)
return nil, vcsErrorf("unexpected response from hg log: %q", out)
}
hash := f[0]
version := rev
@ -401,7 +468,7 @@ func hgParseStat(rev, out string) (*RevInfo, error) {
}
t, err := strconv.ParseInt(f[1], 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid time from hg log: %q", out)
return nil, vcsErrorf("invalid time from hg log: %q", out)
}
var tags []string
@ -430,12 +497,12 @@ func svnParseStat(rev, out string) (*RevInfo, error) {
} `xml:"logentry"`
}
if err := xml.Unmarshal([]byte(out), &log); err != nil {
return nil, fmt.Errorf("unexpected response from svn log --xml: %v\n%s", err, out)
return nil, vcsErrorf("unexpected response from svn log --xml: %v\n%s", err, out)
}
t, err := time.Parse(time.RFC3339, log.Logentry.Date)
if err != nil {
return nil, fmt.Errorf("unexpected response from svn log --xml: %v\n%s", err, out)
return nil, vcsErrorf("unexpected response from svn log --xml: %v\n%s", err, out)
}
info := &RevInfo{
@ -471,23 +538,23 @@ func bzrParseStat(rev, out string) (*RevInfo, error) {
}
i, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return nil, fmt.Errorf("unexpected revno from bzr log: %q", line)
return nil, vcsErrorf("unexpected revno from bzr log: %q", line)
}
revno = i
case "timestamp":
j := strings.Index(val, " ")
if j < 0 {
return nil, fmt.Errorf("unexpected timestamp from bzr log: %q", line)
return nil, vcsErrorf("unexpected timestamp from bzr log: %q", line)
}
t, err := time.Parse("2006-01-02 15:04:05 -0700", val[j+1:])
if err != nil {
return nil, fmt.Errorf("unexpected timestamp from bzr log: %q", line)
return nil, vcsErrorf("unexpected timestamp from bzr log: %q", line)
}
tm = t.UTC()
}
}
if revno == 0 || tm.IsZero() {
return nil, fmt.Errorf("unexpected response from bzr log: %q", out)
return nil, vcsErrorf("unexpected response from bzr log: %q", out)
}
info := &RevInfo{
@ -504,11 +571,11 @@ func fossilParseStat(rev, out string) (*RevInfo, error) {
if strings.HasPrefix(line, "uuid:") {
f := strings.Fields(line)
if len(f) != 5 || len(f[1]) != 40 || f[4] != "UTC" {
return nil, fmt.Errorf("unexpected response from fossil info: %q", line)
return nil, vcsErrorf("unexpected response from fossil info: %q", line)
}
t, err := time.Parse("2006-01-02 15:04:05", f[2]+" "+f[3])
if err != nil {
return nil, fmt.Errorf("unexpected response from fossil info: %q", line)
return nil, vcsErrorf("unexpected response from fossil info: %q", line)
}
hash := f[1]
version := rev
@ -524,5 +591,5 @@ func fossilParseStat(rev, out string) (*RevInfo, error) {
return info, nil
}
}
return nil, fmt.Errorf("unexpected response from fossil info: %q", out)
return nil, vcsErrorf("unexpected response from fossil info: %q", out)
}

View File

@ -407,25 +407,26 @@ func (r *codeRepo) modPrefix(rev string) string {
return r.modPath + "@" + rev
}
func (r *codeRepo) Zip(version string, tmpdir string) (tmpfile string, err error) {
func (r *codeRepo) Zip(dst io.Writer, version string) error {
rev, dir, _, err := r.findDir(version)
if err != nil {
return "", err
return err
}
dl, actualDir, err := r.code.ReadZip(rev, dir, codehost.MaxZipFile)
if err != nil {
return "", err
return err
}
defer dl.Close()
if actualDir != "" && !hasPathPrefix(dir, actualDir) {
return "", fmt.Errorf("internal error: downloading %v %v: dir=%q but actualDir=%q", r.path, rev, dir, actualDir)
return fmt.Errorf("internal error: downloading %v %v: dir=%q but actualDir=%q", r.path, rev, dir, actualDir)
}
subdir := strings.Trim(strings.TrimPrefix(dir, actualDir), "/")
// Spool to local file.
f, err := ioutil.TempFile(tmpdir, "go-codehost-")
f, err := ioutil.TempFile("", "go-codehost-")
if err != nil {
dl.Close()
return "", err
return err
}
defer os.Remove(f.Name())
defer f.Close()
@ -433,35 +434,24 @@ func (r *codeRepo) Zip(version string, tmpdir string) (tmpfile string, err error
lr := &io.LimitedReader{R: dl, N: maxSize + 1}
if _, err := io.Copy(f, lr); err != nil {
dl.Close()
return "", err
return err
}
dl.Close()
if lr.N <= 0 {
return "", fmt.Errorf("downloaded zip file too large")
return fmt.Errorf("downloaded zip file too large")
}
size := (maxSize + 1) - lr.N
if _, err := f.Seek(0, 0); err != nil {
return "", err
return err
}
// Translate from zip file we have to zip file we want.
zr, err := zip.NewReader(f, size)
if err != nil {
return "", err
}
f2, err := ioutil.TempFile(tmpdir, "go-codezip-")
if err != nil {
return "", err
return err
}
zw := zip.NewWriter(f2)
newName := f2.Name()
defer func() {
f2.Close()
if err != nil {
os.Remove(newName)
}
}()
zw := zip.NewWriter(dst)
if subdir != "" {
subdir += "/"
}
@ -472,12 +462,12 @@ func (r *codeRepo) Zip(version string, tmpdir string) (tmpfile string, err error
if topPrefix == "" {
i := strings.Index(zf.Name, "/")
if i < 0 {
return "", fmt.Errorf("missing top-level directory prefix")
return fmt.Errorf("missing top-level directory prefix")
}
topPrefix = zf.Name[:i+1]
}
if !strings.HasPrefix(zf.Name, topPrefix) {
return "", fmt.Errorf("zip file contains more than one top-level directory")
return fmt.Errorf("zip file contains more than one top-level directory")
}
dir, file := path.Split(zf.Name)
if file == "go.mod" {
@ -497,11 +487,17 @@ func (r *codeRepo) Zip(version string, tmpdir string) (tmpfile string, err error
name = dir[:len(dir)-1]
}
}
for _, zf := range zr.File {
if !zf.FileInfo().Mode().IsRegular() {
// Skip symlinks (golang.org/issue/27093).
continue
}
if topPrefix == "" {
i := strings.Index(zf.Name, "/")
if i < 0 {
return "", fmt.Errorf("missing top-level directory prefix")
return fmt.Errorf("missing top-level directory prefix")
}
topPrefix = zf.Name[:i+1]
}
@ -509,7 +505,7 @@ func (r *codeRepo) Zip(version string, tmpdir string) (tmpfile string, err error
continue
}
if !strings.HasPrefix(zf.Name, topPrefix) {
return "", fmt.Errorf("zip file contains more than one top-level directory")
return fmt.Errorf("zip file contains more than one top-level directory")
}
name := strings.TrimPrefix(zf.Name, topPrefix)
if !strings.HasPrefix(name, subdir) {
@ -529,28 +525,28 @@ func (r *codeRepo) Zip(version string, tmpdir string) (tmpfile string, err error
}
base := path.Base(name)
if strings.ToLower(base) == "go.mod" && base != "go.mod" {
return "", fmt.Errorf("zip file contains %s, want all lower-case go.mod", zf.Name)
return fmt.Errorf("zip file contains %s, want all lower-case go.mod", zf.Name)
}
if name == "LICENSE" {
haveLICENSE = true
}
size := int64(zf.UncompressedSize)
size := int64(zf.UncompressedSize64)
if size < 0 || maxSize < size {
return "", fmt.Errorf("module source tree too big")
return fmt.Errorf("module source tree too big")
}
maxSize -= size
rc, err := zf.Open()
if err != nil {
return "", err
return err
}
w, err := zw.Create(r.modPrefix(version) + "/" + name)
lr := &io.LimitedReader{R: rc, N: size + 1}
if _, err := io.Copy(w, lr); err != nil {
return "", err
return err
}
if lr.N <= 0 {
return "", fmt.Errorf("individual file too large")
return fmt.Errorf("individual file too large")
}
}
@ -559,21 +555,15 @@ func (r *codeRepo) Zip(version string, tmpdir string) (tmpfile string, err error
if err == nil {
w, err := zw.Create(r.modPrefix(version) + "/LICENSE")
if err != nil {
return "", err
return err
}
if _, err := w.Write(data); err != nil {
return "", err
return err
}
}
}
if err := zw.Close(); err != nil {
return "", err
}
if err := f2.Close(); err != nil {
return "", err
}
return f2.Name(), nil
return zw.Close()
}
// hasPathPrefix reports whether the path s begins with the

View File

@ -284,10 +284,10 @@ var codeRepoTests = []struct {
{
path: "gopkg.in/yaml.v2",
rev: "v2",
version: "v2.2.1",
name: "5420a8b6744d3b0345ab293f6fcba19c978f1183",
short: "5420a8b6744d",
time: time.Date(2018, 3, 28, 19, 50, 20, 0, time.UTC),
version: "v2.2.2",
name: "51d6538a90f86fe93ac480b35f37b2be17fef232",
short: "51d6538a90f8",
time: time.Date(2018, 11, 15, 11, 05, 04, 0, time.UTC),
gomod: "module \"gopkg.in/yaml.v2\"\n\nrequire (\n\t\"gopkg.in/check.v1\" v0.0.0-20161208181325-20d25e280405\n)\n",
},
{
@ -391,7 +391,13 @@ func TestCodeRepo(t *testing.T) {
}
}
if tt.zip != nil || tt.ziperr != "" {
zipfile, err := repo.Zip(tt.version, tmpdir)
f, err := ioutil.TempFile(tmpdir, tt.version+".zip.")
if err != nil {
t.Fatalf("ioutil.TempFile: %v", err)
}
zipfile := f.Name()
err = repo.Zip(f, tt.version)
f.Close()
if err != nil {
if tt.ziperr != "" {
if err.Error() == tt.ziperr {
@ -423,7 +429,7 @@ func TestCodeRepo(t *testing.T) {
}
}
}
t.Run(strings.Replace(tt.path, "/", "_", -1)+"/"+tt.rev, f)
t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.rev, f)
if strings.HasPrefix(tt.path, vgotest1git) {
for _, alt := range altVgotests {
// Note: Communicating with f through tt; should be cleaned up.
@ -442,7 +448,7 @@ func TestCodeRepo(t *testing.T) {
tt.rev = remap(tt.rev, m)
tt.gomoderr = remap(tt.gomoderr, m)
tt.ziperr = remap(tt.ziperr, m)
t.Run(strings.Replace(tt.path, "/", "_", -1)+"/"+tt.rev, f)
t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.rev, f)
tt = old
}
}
@ -473,9 +479,9 @@ func remap(name string, m map[string]string) string {
}
}
for k, v := range m {
name = strings.Replace(name, k, v, -1)
name = strings.ReplaceAll(name, k, v)
if codehost.AllHex(k) {
name = strings.Replace(name, k[:12], v[:12], -1)
name = strings.ReplaceAll(name, k[:12], v[:12])
}
}
return name
@ -505,11 +511,11 @@ var codeRepoVersionsTests = []struct {
},
{
path: "gopkg.in/russross/blackfriday.v2",
versions: []string{"v2.0.0"},
versions: []string{"v2.0.0", "v2.0.1"},
},
{
path: "gopkg.in/natefinch/lumberjack.v2",
versions: nil,
versions: []string{"v2.0.0"},
},
}
@ -522,7 +528,7 @@ func TestCodeRepoVersions(t *testing.T) {
}
defer os.RemoveAll(tmpdir)
for _, tt := range codeRepoVersionsTests {
t.Run(strings.Replace(tt.path, "/", "_", -1), func(t *testing.T) {
t.Run(strings.ReplaceAll(tt.path, "/", "_"), func(t *testing.T) {
repo, err := Lookup(tt.path)
if err != nil {
t.Fatalf("Lookup(%q): %v", tt.path, err)
@ -570,7 +576,7 @@ func TestLatest(t *testing.T) {
}
defer os.RemoveAll(tmpdir)
for _, tt := range latestTests {
name := strings.Replace(tt.path, "/", "_", -1)
name := strings.ReplaceAll(tt.path, "/", "_")
t.Run(name, func(t *testing.T) {
repo, err := Lookup(tt.path)
if err != nil {

View File

@ -21,6 +21,7 @@ import (
"cmd/go/internal/dirhash"
"cmd/go/internal/module"
"cmd/go/internal/par"
"cmd/go/internal/renameio"
)
var downloadCache par.Cache
@ -34,9 +35,7 @@ func Download(mod module.Version) (dir string, err error) {
return "", fmt.Errorf("missing modfetch.PkgMod")
}
// The par.Cache here avoids duplicate work but also
// avoids conflicts from simultaneous calls by multiple goroutines
// for the same version.
// The par.Cache here avoids duplicate work.
type cached struct {
dir string
err error
@ -46,16 +45,8 @@ func Download(mod module.Version) (dir string, err error) {
if err != nil {
return cached{"", err}
}
if files, _ := ioutil.ReadDir(dir); len(files) == 0 {
zipfile, err := DownloadZip(mod)
if err != nil {
return cached{"", err}
}
modpath := mod.Path + "@" + mod.Version
if err := Unzip(dir, zipfile, modpath, 0); err != nil {
fmt.Fprintf(os.Stderr, "-> %s\n", err)
return cached{"", err}
}
if err := download(mod, dir); err != nil {
return cached{"", err}
}
checkSum(mod)
return cached{dir, nil}
@ -63,14 +54,88 @@ func Download(mod module.Version) (dir string, err error) {
return c.dir, c.err
}
func download(mod module.Version, dir string) (err error) {
// If the directory exists, the module has already been extracted.
fi, err := os.Stat(dir)
if err == nil && fi.IsDir() {
return nil
}
// To avoid cluttering the cache with extraneous files,
// DownloadZip uses the same lockfile as Download.
// Invoke DownloadZip before locking the file.
zipfile, err := DownloadZip(mod)
if err != nil {
return err
}
if cfg.CmdName != "mod download" {
fmt.Fprintf(os.Stderr, "go: extracting %s %s\n", mod.Path, mod.Version)
}
unlock, err := lockVersion(mod)
if err != nil {
return err
}
defer unlock()
// Check whether the directory was populated while we were waiting on the lock.
fi, err = os.Stat(dir)
if err == nil && fi.IsDir() {
return nil
}
// Clean up any remaining temporary directories from previous runs.
// This is only safe to do because the lock file ensures that their writers
// are no longer active.
parentDir := filepath.Dir(dir)
tmpPrefix := filepath.Base(dir) + ".tmp-"
if old, err := filepath.Glob(filepath.Join(parentDir, tmpPrefix+"*")); err == nil {
for _, path := range old {
RemoveAll(path) // best effort
}
}
// 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.
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)
}
}()
modpath := mod.Path + "@" + mod.Version
if err := Unzip(tmpDir, zipfile, modpath, 0); err != nil {
fmt.Fprintf(os.Stderr, "-> %s\n", err)
return err
}
if err := os.Rename(tmpDir, dir); err != nil {
return err
}
// Make dir read-only only *after* renaming it.
// os.Rename was observed to fail for read-only directories on macOS.
makeDirsReadOnly(dir)
return nil
}
var downloadZipCache par.Cache
// DownloadZip downloads the specific module version to the
// local zip cache and returns the name of the zip file.
func DownloadZip(mod module.Version) (zipfile string, err error) {
// The par.Cache here avoids duplicate work but also
// avoids conflicts from simultaneous calls by multiple goroutines
// for the same version.
// The par.Cache here avoids duplicate work.
type cached struct {
zipfile string
err error
@ -80,83 +145,134 @@ func DownloadZip(mod module.Version) (zipfile string, err error) {
if err != nil {
return cached{"", err}
}
// Skip locking if the zipfile already exists.
if _, err := os.Stat(zipfile); err == nil {
// Use it.
// This should only happen if the mod/cache directory is preinitialized
// or if pkg/mod/path was removed but not pkg/mod/cache/download.
if cfg.CmdName != "mod download" {
fmt.Fprintf(os.Stderr, "go: extracting %s %s\n", mod.Path, mod.Version)
}
} else {
if err := os.MkdirAll(filepath.Dir(zipfile), 0777); err != nil {
return cached{"", err}
}
if cfg.CmdName != "mod download" {
fmt.Fprintf(os.Stderr, "go: downloading %s %s\n", mod.Path, mod.Version)
}
if err := downloadZip(mod, zipfile); err != nil {
return cached{"", err}
}
return cached{zipfile, nil}
}
// The zip file does not exist. Acquire the lock and create it.
if cfg.CmdName != "mod download" {
fmt.Fprintf(os.Stderr, "go: downloading %s %s\n", mod.Path, mod.Version)
}
unlock, err := lockVersion(mod)
if err != nil {
return cached{"", err}
}
defer unlock()
// Double-check that the zipfile was not created while we were waiting for
// the lock.
if _, err := os.Stat(zipfile); err == nil {
return cached{zipfile, nil}
}
if err := os.MkdirAll(filepath.Dir(zipfile), 0777); err != nil {
return cached{"", err}
}
if err := downloadZip(mod, zipfile); err != nil {
return cached{"", err}
}
return cached{zipfile, nil}
}).(cached)
return c.zipfile, c.err
}
func downloadZip(mod module.Version, target string) error {
func downloadZip(mod module.Version, zipfile string) (err error) {
// Clean up any remaining tempfiles from previous runs.
// This is only safe to do because the lock file ensures that their
// writers are no longer active.
for _, base := range []string{zipfile, zipfile + "hash"} {
if old, err := filepath.Glob(renameio.Pattern(base)); err == nil {
for _, path := range old {
os.Remove(path) // best effort
}
}
}
// From here to the os.Rename call below is functionally almost equivalent to
// renameio.WriteToFile, with one key difference: we want to validate the
// contents of the file (by hashing it) before we commit it. Because the file
// is zip-compressed, we need an actual file — or at least an io.ReaderAt — to
// validate it: we can't just tee the stream as we write it.
f, err := ioutil.TempFile(filepath.Dir(zipfile), filepath.Base(renameio.Pattern(zipfile)))
if err != nil {
return err
}
defer func() {
if err != nil {
f.Close()
os.Remove(f.Name())
}
}()
repo, err := Lookup(mod.Path)
if err != nil {
return err
}
tmpfile, err := repo.Zip(mod.Version, os.TempDir())
if err != nil {
if err := repo.Zip(f, mod.Version); err != nil {
return err
}
defer os.Remove(tmpfile)
// Double-check zip file looks OK.
z, err := zip.OpenReader(tmpfile)
// Double-check that the paths within the zip file are well-formed.
//
// TODO(bcmills): There is a similar check within the Unzip function. Can we eliminate one?
fi, err := f.Stat()
if err != nil {
return err
}
prefix := mod.Path + "@" + mod.Version
z, err := zip.NewReader(f, fi.Size())
if err != nil {
return err
}
prefix := mod.Path + "@" + mod.Version + "/"
for _, f := range z.File {
if !strings.HasPrefix(f.Name, prefix) {
z.Close()
return fmt.Errorf("zip for %s has unexpected file %s", prefix[:len(prefix)-1], f.Name)
}
}
z.Close()
hash, err := dirhash.HashZip(tmpfile, dirhash.DefaultHash)
// Sync the file before renaming it: otherwise, after a crash the reader may
// observe a 0-length file instead of the actual contents.
// See https://golang.org/issue/22397#issuecomment-380831736.
if err := f.Sync(); err != nil {
return err
}
if err := f.Close(); err != nil {
return err
}
// Hash the zip file and check the sum before renaming to the final location.
hash, err := dirhash.HashZip(f.Name(), dirhash.DefaultHash)
if err != nil {
return err
}
checkOneSum(mod, hash) // check before installing the zip file
r, err := os.Open(tmpfile)
if err != nil {
checkOneSum(mod, hash)
if err := renameio.WriteFile(zipfile+"hash", []byte(hash)); err != nil {
return err
}
defer r.Close()
w, err := os.Create(target)
if err != nil {
if err := os.Rename(f.Name(), zipfile); err != nil {
return err
}
if _, err := io.Copy(w, r); err != nil {
w.Close()
return fmt.Errorf("copying: %v", err)
}
if err := w.Close(); err != nil {
return err
}
return ioutil.WriteFile(target+"hash", []byte(hash), 0666)
// TODO(bcmills): Should we make the .zip and .ziphash files read-only to discourage tampering?
return nil
}
var GoSumFile string // path to go.sum; set by package modload
type modSum struct {
mod module.Version
sum string
}
var goSum struct {
mu sync.Mutex
m map[module.Version][]string // content of go.sum file (+ go.modverify if present)
checked map[modSum]bool // sums actually checked during execution
dirty bool // whether we added any new sums to m
overwrite bool // if true, overwrite go.sum without incorporating its contents
enabled bool // whether to use go.sum at all
modverify string // path to go.modverify, to be deleted
}
@ -173,18 +289,25 @@ func initGoSum() bool {
}
goSum.m = make(map[module.Version][]string)
goSum.checked = make(map[modSum]bool)
data, err := ioutil.ReadFile(GoSumFile)
if err != nil && !os.IsNotExist(err) {
base.Fatalf("go: %v", err)
}
goSum.enabled = true
readGoSum(GoSumFile, data)
readGoSum(goSum.m, GoSumFile, data)
// Add old go.modverify file.
// We'll delete go.modverify in WriteGoSum.
alt := strings.TrimSuffix(GoSumFile, ".sum") + ".modverify"
if data, err := ioutil.ReadFile(alt); err == nil {
readGoSum(alt, data)
migrate := make(map[module.Version][]string)
readGoSum(migrate, alt, data)
for mod, sums := range migrate {
for _, sum := range sums {
checkOneSumLocked(mod, sum)
}
}
goSum.modverify = alt
}
return true
@ -197,7 +320,7 @@ const emptyGoModHash = "h1:G7mAYYxgmS0lVkHyy2hEOLQCFB0DlQFTMLWggykrydY="
// readGoSum parses data, which is the content of file,
// and adds it to goSum.m. The goSum lock must be held.
func readGoSum(file string, data []byte) {
func readGoSum(dst map[module.Version][]string, file string, data []byte) {
lineno := 0
for len(data) > 0 {
var line []byte
@ -221,7 +344,7 @@ func readGoSum(file string, data []byte) {
continue
}
mod := module.Version{Path: f[0], Version: f[1]}
goSum.m[mod] = append(goSum.m[mod], f[2])
dst[mod] = append(dst[mod], f[2])
}
}
@ -235,7 +358,7 @@ func checkSum(mod module.Version) {
// Do the file I/O before acquiring the go.sum lock.
ziphash, err := CachePath(mod, "ziphash")
if err != nil {
base.Fatalf("go: verifying %s@%s: %v", mod.Path, mod.Version, err)
base.Fatalf("verifying %s@%s: %v", mod.Path, mod.Version, err)
}
data, err := ioutil.ReadFile(ziphash)
if err != nil {
@ -243,11 +366,11 @@ func checkSum(mod module.Version) {
// This can happen if someone does rm -rf GOPATH/src/cache/download. So it goes.
return
}
base.Fatalf("go: verifying %s@%s: %v", mod.Path, mod.Version, err)
base.Fatalf("verifying %s@%s: %v", mod.Path, mod.Version, err)
}
h := strings.TrimSpace(string(data))
if !strings.HasPrefix(h, "h1:") {
base.Fatalf("go: verifying %s@%s: unexpected ziphash: %q", mod.Path, mod.Version, h)
base.Fatalf("verifying %s@%s: unexpected ziphash: %q", mod.Path, mod.Version, h)
}
checkOneSum(mod, h)
@ -265,7 +388,7 @@ func goModSum(data []byte) (string, error) {
func checkGoMod(path, version string, data []byte) {
h, err := goModSum(data)
if err != nil {
base.Fatalf("go: verifying %s %s go.mod: %v", path, version, err)
base.Fatalf("verifying %s %s go.mod: %v", path, version, err)
}
checkOneSum(module.Version{Path: path, Version: version + "/go.mod"}, h)
@ -275,22 +398,27 @@ func checkGoMod(path, version string, data []byte) {
func checkOneSum(mod module.Version, h string) {
goSum.mu.Lock()
defer goSum.mu.Unlock()
if !initGoSum() {
return
if initGoSum() {
checkOneSumLocked(mod, h)
}
}
func checkOneSumLocked(mod module.Version, h string) {
goSum.checked[modSum{mod, h}] = true
for _, vh := range goSum.m[mod] {
if h == vh {
return
}
if strings.HasPrefix(vh, "h1:") {
base.Fatalf("go: verifying %s@%s: checksum mismatch\n\tdownloaded: %v\n\tgo.sum: %v", mod.Path, mod.Version, h, vh)
base.Fatalf("verifying %s@%s: checksum mismatch\n\tdownloaded: %v\n\tgo.sum: %v", mod.Path, mod.Version, h, vh)
}
}
if len(goSum.m[mod]) > 0 {
fmt.Fprintf(os.Stderr, "warning: verifying %s@%s: unknown hashes in go.sum: %v; adding %v", mod.Path, mod.Version, strings.Join(goSum.m[mod], ", "), h)
}
goSum.m[mod] = append(goSum.m[mod], h)
goSum.dirty = true
}
// Sum returns the checksum for the downloaded copy of the given module,
@ -316,9 +444,54 @@ func Sum(mod module.Version) string {
func WriteGoSum() {
goSum.mu.Lock()
defer goSum.mu.Unlock()
if !initGoSum() {
if !goSum.enabled {
// If we haven't read the go.sum file yet, don't bother writing it: at best,
// we could rename the go.modverify file if it isn't empty, but we haven't
// needed to touch it so far — how important could it be?
return
}
if !goSum.dirty {
// Don't bother opening the go.sum file if we don't have anything to add.
return
}
// We want to avoid races between creating the lockfile and deleting it, but
// we also don't want to leave a permanent lockfile in the user's repository.
//
// On top of that, if we crash while writing go.sum, we don't want to lose the
// sums that were already present in the file, so it's important that we write
// the file by renaming rather than truncating — which means that we can't
// lock the go.sum file itself.
//
// Instead, we'll lock a distinguished file in the cache directory: that will
// only race if the user runs `go clean -modcache` concurrently with a command
// that updates go.sum, and that's already racy to begin with.
//
// We'll end up slightly over-synchronizing go.sum writes if the user runs a
// bunch of go commands that update sums in separate modules simultaneously,
// but that's unlikely to matter in practice.
unlock := SideLock()
defer unlock()
if !goSum.overwrite {
// Re-read the go.sum file to incorporate any sums added by other processes
// in the meantime.
data, err := ioutil.ReadFile(GoSumFile)
if err != nil && !os.IsNotExist(err) {
base.Fatalf("go: re-reading go.sum: %v", err)
}
// Add only the sums that we actually checked: the user may have edited or
// truncated the file to remove erroneous hashes, and we shouldn't restore
// them without good reason.
goSum.m = make(map[module.Version][]string, len(goSum.m))
readGoSum(goSum.m, GoSumFile, data)
for ms := range goSum.checked {
checkOneSumLocked(ms.mod, ms.sum)
}
}
var mods []module.Version
for m := range goSum.m {
@ -334,15 +507,16 @@ func WriteGoSum() {
}
}
data, _ := ioutil.ReadFile(GoSumFile)
if !bytes.Equal(data, buf.Bytes()) {
if err := ioutil.WriteFile(GoSumFile, buf.Bytes(), 0666); err != nil {
base.Fatalf("go: writing go.sum: %v", err)
}
if err := renameio.WriteFile(GoSumFile, buf.Bytes()); err != nil {
base.Fatalf("go: writing go.sum: %v", err)
}
goSum.checked = make(map[modSum]bool)
goSum.dirty = false
goSum.overwrite = false
if goSum.modverify != "" {
os.Remove(goSum.modverify)
os.Remove(goSum.modverify) // best effort
}
}
@ -360,6 +534,8 @@ func TrimGoSum(keep map[module.Version]bool) {
noGoMod := module.Version{Path: m.Path, Version: strings.TrimSuffix(m.Version, "/go.mod")}
if !keep[m] && !keep[noGoMod] {
delete(goSum.m, m)
goSum.dirty = true
goSum.overwrite = true
}
}
}

View File

@ -8,7 +8,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/url"
"os"
"strings"
@ -209,44 +208,31 @@ func (p *proxyRepo) GoMod(version string) ([]byte, error) {
return data, nil
}
func (p *proxyRepo) Zip(version string, tmpdir string) (tmpfile string, err error) {
func (p *proxyRepo) Zip(dst io.Writer, version string) error {
var body io.ReadCloser
encVer, err := module.EncodeVersion(version)
if err != nil {
return "", err
return err
}
err = webGetBody(p.url+"/@v/"+pathEscape(encVer)+".zip", &body)
if err != nil {
return "", err
return err
}
defer body.Close()
// Spool to local file.
f, err := ioutil.TempFile(tmpdir, "go-proxy-download-")
if err != nil {
return "", err
}
defer f.Close()
maxSize := int64(codehost.MaxZipFile)
lr := &io.LimitedReader{R: body, N: maxSize + 1}
if _, err := io.Copy(f, lr); err != nil {
os.Remove(f.Name())
return "", err
lr := &io.LimitedReader{R: body, N: codehost.MaxZipFile + 1}
if _, err := io.Copy(dst, lr); err != nil {
return err
}
if lr.N <= 0 {
os.Remove(f.Name())
return "", fmt.Errorf("downloaded zip file too large")
return fmt.Errorf("downloaded zip file too large")
}
if err := f.Close(); err != nil {
os.Remove(f.Name())
return "", err
}
return f.Name(), nil
return nil
}
// pathEscape escapes s so it can be used in a path.
// That is, it escapes things like ? and # (which really shouldn't appear anyway).
// It does not escape / to %2F: our REST API is designed so that / can be left as is.
func pathEscape(s string) string {
return strings.Replace(url.PathEscape(s), "%2F", "/", -1)
return strings.ReplaceAll(url.PathEscape(s), "%2F", "/")
}

View File

@ -6,8 +6,10 @@ package modfetch
import (
"fmt"
"io"
"os"
"sort"
"strconv"
"time"
"cmd/go/internal/cfg"
@ -45,11 +47,8 @@ type Repo interface {
// GoMod returns the go.mod file for the given version.
GoMod(version string) (data []byte, err error)
// Zip downloads a zip file for the given version
// to a new file in a given temporary directory.
// It returns the name of the new file.
// The caller should remove the file when finished with it.
Zip(version, tmpdir string) (tmpfile string, err error)
// Zip writes a zip file for the given version to dst.
Zip(dst io.Writer, version string) error
}
// A Rev describes a single revision in a module repository.
@ -357,7 +356,11 @@ func (l *loggingRepo) GoMod(version string) ([]byte, error) {
return l.r.GoMod(version)
}
func (l *loggingRepo) Zip(version, tmpdir string) (string, error) {
defer logCall("Repo[%s]: Zip(%q, %q)", l.r.ModulePath(), version, tmpdir)()
return l.r.Zip(version, tmpdir)
func (l *loggingRepo) Zip(dst io.Writer, version string) error {
dstName := "_"
if dst, ok := dst.(interface{ Name() string }); ok {
dstName = strconv.Quote(dst.Name())
}
defer logCall("Repo[%s]: Zip(%s, %q)", l.r.ModulePath(), dstName, version)()
return l.r.Zip(dst, version)
}

View File

@ -12,7 +12,6 @@ import (
"os"
"path"
"path/filepath"
"sort"
"strings"
"cmd/go/internal/modfetch/codehost"
@ -21,12 +20,12 @@ import (
)
func Unzip(dir, zipfile, prefix string, maxSize int64) error {
// TODO(bcmills): The maxSize parameter is invariantly 0. Remove it.
if maxSize == 0 {
maxSize = codehost.MaxZipFile
}
// Directory can exist, but must be empty.
// except maybe
files, _ := ioutil.ReadDir(dir)
if len(files) > 0 {
return fmt.Errorf("target directory %v exists and is not empty", dir)
@ -98,22 +97,16 @@ func Unzip(dir, zipfile, prefix string, maxSize int64) error {
}
// Unzip, enforcing sizes checked earlier.
dirs := map[string]bool{dir: true}
for _, zf := range z.File {
if zf.Name == prefix || strings.HasSuffix(zf.Name, "/") {
continue
}
name := zf.Name[len(prefix):]
dst := filepath.Join(dir, name)
parent := filepath.Dir(dst)
for parent != dir {
dirs[parent] = true
parent = filepath.Dir(parent)
}
if err := os.MkdirAll(filepath.Dir(dst), 0777); err != nil {
return err
}
w, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0444)
w, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0444)
if err != nil {
return fmt.Errorf("unzip %v: %v", zipfile, err)
}
@ -137,17 +130,44 @@ func Unzip(dir, zipfile, prefix string, maxSize int64) error {
}
}
// Mark directories unwritable, best effort.
var dirlist []string
for dir := range dirs {
dirlist = append(dirlist, dir)
}
sort.Strings(dirlist)
// Run over list backward to chmod children before parents.
for i := len(dirlist) - 1; i >= 0; i-- {
os.Chmod(dirlist[i], 0555)
}
return nil
}
// makeDirsReadOnly makes a best-effort attempt to remove write permissions for dir
// and its transitive contents.
func makeDirsReadOnly(dir string) {
type pathMode struct {
path string
mode os.FileMode
}
var dirs []pathMode // in lexical order
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err == nil && info.Mode()&0222 != 0 {
if info.IsDir() {
dirs = append(dirs, pathMode{path, info.Mode()})
}
}
return nil
})
// Run over list backward to chmod children before parents.
for i := len(dirs) - 1; i >= 0; i-- {
os.Chmod(dirs[i].path, dirs[i].mode&^0222)
}
}
// RemoveAll removes a directory written by Download or Unzip, first applying
// any permission changes needed to do so.
func RemoveAll(dir string) error {
// Module cache has 0555 directories; make them writable in order to remove content.
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil // ignore errors walking in file system
}
if info.IsDir() {
os.Chmod(path, 0777)
}
return nil
})
return os.RemoveAll(dir)
}

View File

@ -154,7 +154,7 @@ func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (*File
return f, nil
}
var goVersionRE = regexp.MustCompile(`([1-9][0-9]*)\.(0|[1-9][0-9]*)`)
var GoVersionRE = regexp.MustCompile(`([1-9][0-9]*)\.(0|[1-9][0-9]*)`)
func (f *File) add(errs *bytes.Buffer, line *Line, verb string, args []string, fix VersionFixer, strict bool) {
// If strict is false, this module is a dependency.
@ -181,7 +181,7 @@ func (f *File) add(errs *bytes.Buffer, line *Line, verb string, args []string, f
fmt.Fprintf(errs, "%s:%d: repeated go statement\n", f.Syntax.Name, line.Start.Line)
return
}
if len(args) != 1 || !goVersionRE.MatchString(args[0]) {
if len(args) != 1 || !GoVersionRE.MatchString(args[0]) {
fmt.Fprintf(errs, "%s:%d: usage: go 1.23\n", f.Syntax.Name, line.Start.Line)
return
}
@ -477,6 +477,22 @@ func (f *File) Cleanup() {
f.Syntax.Cleanup()
}
func (f *File) AddGoStmt(version string) error {
if !GoVersionRE.MatchString(version) {
return fmt.Errorf("invalid language version string %q", version)
}
if f.Go == nil {
f.Go = &Go{
Version: version,
Syntax: f.Syntax.addLine(nil, "go", version),
}
} else {
f.Go.Version = version
f.Syntax.updateLine(f.Go.Syntax, "go", version)
}
return nil
}
func (f *File) AddRequire(path, vers string) error {
need := true
for _, r := range f.Require {

View File

@ -78,7 +78,7 @@ to use newer patch releases when available. Continuing the previous example,
In general, adding a new dependency may require upgrading
existing dependencies to keep a working build, and 'go get' does
this automatically. Similarly, downgrading one dependency may
require downgrading other dependenceis, and 'go get' does
require downgrading other dependencies, and 'go get' does
this automatically as well.
The -m flag instructs get to stop here, after resolving, upgrading,
@ -247,7 +247,7 @@ func runGet(cmd *base.Command, args []string) {
// Deciding which module to upgrade/downgrade for a particular argument is difficult.
// Patterns only make it more difficult.
// We impose restrictions to avoid needing to interlace pattern expansion,
// like in in modload.ImportPaths.
// like in modload.ImportPaths.
// Specifically, these patterns are supported:
//
// - Relative paths like ../../foo or ../../foo... are restricted to matching directories
@ -281,8 +281,8 @@ func runGet(cmd *base.Command, args []string) {
base.Errorf("go get %s: %v", arg, err)
continue
}
if !str.HasFilePathPrefix(abs, modload.ModRoot) {
base.Errorf("go get %s: directory %s is outside module root %s", arg, abs, modload.ModRoot)
if !str.HasFilePathPrefix(abs, modload.ModRoot()) {
base.Errorf("go get %s: directory %s is outside module root %s", arg, abs, modload.ModRoot())
continue
}
// TODO: Check if abs is inside a nested module.
@ -534,9 +534,11 @@ func runGet(cmd *base.Command, args []string) {
// module root.
continue
}
base.Errorf("%s", p.Error)
}
todo = append(todo, p)
}
base.ExitIfErrors()
// If -d was specified, we're done after the download: no build.
// (The load.PackagesAndErrors is what did the download

View File

@ -17,6 +17,7 @@ import (
"internal/goroot"
"os"
"path/filepath"
"runtime/debug"
"strings"
)
@ -30,6 +31,9 @@ func isStandardImportPath(path string) bool {
}
func findStandardImportPath(path string) string {
if path == "" {
panic("findStandardImportPath called with empty path")
}
if search.IsStandardImportPath(path) {
if goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) {
return filepath.Join(cfg.GOROOT, "src", path)
@ -95,11 +99,13 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
Path: m.Path,
Version: m.Version,
Main: true,
Dir: ModRoot,
GoMod: filepath.Join(ModRoot, "go.mod"),
}
if modFile.Go != nil {
info.GoVersion = modFile.Go.Version
if HasModRoot() {
info.Dir = ModRoot()
info.GoMod = filepath.Join(info.Dir, "go.mod")
if modFile.Go != nil {
info.GoVersion = modFile.Go.Version
}
}
return info
}
@ -114,7 +120,7 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
}
if cfg.BuildMod == "vendor" {
info.Dir = filepath.Join(ModRoot, "vendor", m.Path)
info.Dir = filepath.Join(ModRoot(), "vendor", m.Path)
return info
}
@ -142,34 +148,38 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
}
}
}
if cfg.BuildMod == "vendor" {
m.Dir = filepath.Join(ModRoot, "vendor", m.Path)
}
}
complete(info)
if fromBuildList {
if r := Replacement(m); r.Path != "" {
info.Replace = &modinfo.ModulePublic{
Path: r.Path,
Version: r.Version,
GoVersion: info.GoVersion,
}
if r.Version == "" {
if filepath.IsAbs(r.Path) {
info.Replace.Dir = r.Path
} else {
info.Replace.Dir = filepath.Join(ModRoot, r.Path)
}
}
complete(info.Replace)
info.Dir = info.Replace.Dir
info.GoMod = filepath.Join(info.Dir, "go.mod")
info.Error = nil // ignore error loading original module version (it has been replaced)
}
if !fromBuildList {
complete(info)
return info
}
r := Replacement(m)
if r.Path == "" {
complete(info)
return info
}
// Don't hit the network to fill in extra data for replaced modules.
// The original resolved Version and Time don't matter enough to be
// worth the cost, and we're going to overwrite the GoMod and Dir from the
// replacement anyway. See https://golang.org/issue/27859.
info.Replace = &modinfo.ModulePublic{
Path: r.Path,
Version: r.Version,
GoVersion: info.GoVersion,
}
if r.Version == "" {
if filepath.IsAbs(r.Path) {
info.Replace.Dir = r.Path
} else {
info.Replace.Dir = filepath.Join(ModRoot(), r.Path)
}
}
complete(info.Replace)
info.Dir = info.Replace.Dir
info.GoMod = filepath.Join(info.Dir, "go.mod")
return info
}
@ -177,6 +187,7 @@ func PackageBuildInfo(path string, deps []string) string {
if isStandardImportPath(path) || !Enabled() {
return ""
}
target := findModule(path, path)
mdeps := make(map[module.Version]bool)
for _, dep := range deps {
@ -216,28 +227,33 @@ func PackageBuildInfo(path string, deps []string) string {
return buf.String()
}
// findModule returns the module containing the package at path,
// needed to build the package at target.
func findModule(target, path string) module.Version {
// TODO: This should use loaded.
if path == "." {
return buildList[0]
}
for _, mod := range buildList {
if maybeInModule(path, mod.Path) {
return mod
pkg, ok := loaded.pkgCache.Get(path).(*loadPkg)
if ok {
if pkg.err != nil {
base.Fatalf("build %v: cannot load %v: %v", target, path, pkg.err)
}
return pkg.mod
}
if path == "command-line-arguments" {
return Target
}
if printStackInDie {
debug.PrintStack()
}
base.Fatalf("build %v: cannot find module for path %v", target, path)
panic("unreachable")
}
func ModInfoProg(info string) []byte {
return []byte(fmt.Sprintf(`
package main
import _ "unsafe"
//go:linkname __debug_modinfo__ runtime/debug.modinfo
var __debug_modinfo__ string
func init() {
__debug_modinfo__ = %q
}
return []byte(fmt.Sprintf(`package main
import _ "unsafe"
//go:linkname __set_debug_modinfo__ runtime..z2fdebug.setmodinfo
func __set_debug_modinfo__(string)
func init() { __set_debug_modinfo__(%q) }
`, string(infoStart)+info+string(infoEnd)))
}

View File

@ -393,17 +393,20 @@ no /* */ comments. Each line holds a single directive, made up of a
verb followed by arguments. For example:
module my/thing
go 1.12
require other/thing v1.0.2
require new/thing v2.3.4
require new/thing/v2 v2.3.4
exclude old/thing v1.2.3
replace bad/thing v1.4.5 => good/thing v1.4.5
The verbs are module, to define the module path; require, to require
a particular module at a given version or later; exclude, to exclude
a particular module version from use; and replace, to replace a module
version with a different module version. Exclude and replace apply only
in the main module's go.mod and are ignored in dependencies.
See https://research.swtch.com/vgo-mvs for details.
The verbs are
module, to define the module path;
go, to set the expected language version;
require, to require a particular module at a given version or later;
exclude, to exclude a particular module version from use; and
replace, to replace a module version with a different module version.
Exclude and replace apply only in the main module's go.mod and are ignored
in dependencies. See https://research.swtch.com/vgo-mvs for details.
The leading verb can be factored out of adjacent lines to create a block,
like in Go imports:

View File

@ -12,13 +12,17 @@ import (
"internal/goroot"
"os"
"path/filepath"
"sort"
"strings"
"time"
"cmd/go/internal/cfg"
"cmd/go/internal/modfetch"
"cmd/go/internal/modfetch/codehost"
"cmd/go/internal/module"
"cmd/go/internal/par"
"cmd/go/internal/search"
"cmd/go/internal/semver"
)
type ImportMissingError struct {
@ -58,9 +62,6 @@ func Import(path string) (m module.Version, dir string, err error) {
// Is the package in the standard library?
if search.IsStandardImportPath(path) {
if strings.HasPrefix(path, "golang_org/") {
return module.Version{}, filepath.Join(cfg.GOROOT, "src/vendor", path), nil
}
if goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) {
dir := filepath.Join(cfg.GOROOT, "src", path)
return module.Version{}, dir, nil
@ -70,8 +71,8 @@ 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, Target.Path, ModRoot, true)
vendorDir, vendorOK := dirInModule(path, "", filepath.Join(ModRoot, "vendor"), false)
mainDir, mainOK := dirInModule(path, Target.Path, ModRoot(), true)
vendorDir, vendorOK := dirInModule(path, "", filepath.Join(ModRoot(), "vendor"), false)
if mainOK && vendorOK {
return module.Version{}, "", fmt.Errorf("ambiguous import: found %s in multiple directories:\n\t%s\n\t%s", path, mainDir, vendorDir)
}
@ -125,14 +126,58 @@ func Import(path string) (m module.Version, dir string, err error) {
return module.Version{}, "", errors.New(buf.String())
}
// Not on build list.
// Look up module containing the package, for addition to the build list.
// Goal is to determine the module, download it to dir, and return m, dir, ErrMissing.
if cfg.BuildMod == "readonly" {
return module.Version{}, "", fmt.Errorf("import lookup disabled by -mod=%s", cfg.BuildMod)
}
// Not on build list.
// To avoid spurious remote fetches, next try the latest replacement for each module.
// (golang.org/issue/26241)
if modFile != nil {
latest := map[string]string{} // path -> version
for _, r := range modFile.Replace {
if maybeInModule(path, r.Old.Path) {
latest[r.Old.Path] = semver.Max(r.Old.Version, latest[r.Old.Path])
}
}
mods = make([]module.Version, 0, len(latest))
for p, v := range latest {
// If the replacement didn't specify a version, synthesize a
// pseudo-version with an appropriate major version and a timestamp below
// any real timestamp. That way, if the main module is used from within
// some other module, the user will be able to upgrade the requirement to
// any real version they choose.
if v == "" {
if _, pathMajor, ok := module.SplitPathVersion(p); ok && len(pathMajor) > 0 {
v = modfetch.PseudoVersion(pathMajor[1:], "", time.Time{}, "000000000000")
} else {
v = modfetch.PseudoVersion("v0", "", time.Time{}, "000000000000")
}
}
mods = append(mods, module.Version{Path: p, Version: v})
}
// Every module path in mods is a prefix of the import path.
// As in QueryPackage, prefer the longest prefix that satisfies the import.
sort.Slice(mods, func(i, j int) bool {
return len(mods[i].Path) > len(mods[j].Path)
})
for _, m := range mods {
root, isLocal, err := fetch(m)
if err != nil {
// Report fetch error as above.
return module.Version{}, "", err
}
_, ok := dirInModule(path, m.Path, root, isLocal)
if ok {
return m, "", &ImportMissingError{ImportPath: path, Module: m}
}
}
}
m, _, err = QueryPackage(path, "latest", Allowed)
if err != nil {
if _, ok := err.(*codehost.VCSError); ok {

View File

@ -45,7 +45,7 @@ func TestImport(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
for _, tt := range importTests {
t.Run(strings.Replace(tt.path, "/", "_", -1), func(t *testing.T) {
t.Run(strings.ReplaceAll(tt.path, "/", "_"), func(t *testing.T) {
// Note that there is no build list, so Import should always fail.
m, dir, err := Import(tt.path)
if err == nil {

View File

@ -16,27 +16,31 @@ import (
"cmd/go/internal/modfile"
"cmd/go/internal/module"
"cmd/go/internal/mvs"
"cmd/go/internal/renameio"
"cmd/go/internal/search"
"encoding/json"
"fmt"
"go/build"
"io/ioutil"
"os"
"path"
"path/filepath"
"regexp"
"runtime/debug"
"strconv"
"strings"
)
var (
cwd string
cwd string // TODO(bcmills): Is this redundant with base.Cwd?
MustUseModules = mustUseModules()
initialized bool
ModRoot string
modFile *modfile.File
excluded map[module.Version]bool
Target module.Version
modRoot string
modFile *modfile.File
modFileData []byte
excluded map[module.Version]bool
Target module.Version
gopath string
@ -53,11 +57,15 @@ var (
// To make permanent changes to the require statements
// in go.mod, edit it before calling ImportPaths or LoadBuildList.
func ModFile() *modfile.File {
Init()
if modFile == nil {
die()
}
return modFile
}
func BinDir() string {
MustInit()
Init()
return filepath.Join(gopath, "bin")
}
@ -73,6 +81,10 @@ func mustUseModules() bool {
var inGOPATH bool // running in GOPATH/src
// Init determines whether module mode is enabled, locates the root of the
// current module (if any), sets environment variables for Git subprocesses, and
// configures the cfg, codehost, load, modfetch, and search packages for use
// with modules.
func Init() {
if initialized {
return
@ -138,6 +150,9 @@ func Init() {
}
if inGOPATH && !MustUseModules {
if CmdModInit {
die() // Don't init a module that we're just going to ignore.
}
// No automatic enabling in GOPATH.
if root, _ := FindModuleRoot(cwd, "", false); root != "" {
cfg.GoModInGOPATH = filepath.Join(root, "go.mod")
@ -147,95 +162,33 @@ func Init() {
if CmdModInit {
// Running 'go mod init': go.mod will be created in current directory.
ModRoot = cwd
modRoot = cwd
} else {
ModRoot, _ = FindModuleRoot(cwd, "", MustUseModules)
if !MustUseModules {
if ModRoot == "" {
return
}
if search.InDir(ModRoot, os.TempDir()) == "." {
// If you create /tmp/go.mod for experimenting,
// then any tests that create work directories under /tmp
// will find it and get modules when they're not expecting them.
// It's a bit of a peculiar thing to disallow but quite mysterious
// when it happens. See golang.org/issue/26708.
ModRoot = ""
fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir())
modRoot, _ = FindModuleRoot(cwd, "", MustUseModules)
if modRoot == "" {
if !MustUseModules {
// GO111MODULE is 'auto' (or unset), and we can't find a module root.
// Stay in GOPATH mode.
return
}
} else if search.InDir(modRoot, os.TempDir()) == "." {
// If you create /tmp/go.mod for experimenting,
// then any tests that create work directories under /tmp
// will find it and get modules when they're not expecting them.
// It's a bit of a peculiar thing to disallow but quite mysterious
// when it happens. See golang.org/issue/26708.
modRoot = ""
fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir())
}
}
cfg.ModulesEnabled = true
load.ModBinDir = BinDir
load.ModLookup = Lookup
load.ModPackageModuleInfo = PackageModuleInfo
load.ModImportPaths = ImportPaths
load.ModPackageBuildInfo = PackageBuildInfo
load.ModInfoProg = ModInfoProg
load.ModImportFromFiles = ImportFromFiles
load.ModDirImportPath = DirImportPath
// We're in module mode. Install the hooks to make it work.
search.SetModRoot(ModRoot)
}
func init() {
load.ModInit = Init
// Set modfetch.PkgMod unconditionally, so that go clean -modcache 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")
}
}
// Enabled reports whether modules are (or must be) enabled.
// If modules must be enabled but are not, Enabled returns true
// and then the first use of module information will call die
// (usually through InitMod and MustInit).
func Enabled() bool {
if !initialized {
panic("go: Enabled called before Init")
}
return ModRoot != "" || MustUseModules
}
// MustInit calls Init if needed and checks that
// modules are enabled and the main module has been found.
// If not, MustInit calls base.Fatalf with an appropriate message.
func MustInit() {
if Init(); ModRoot == "" {
die()
}
if c := cache.Default(); c == nil {
// With modules, there are no install locations for packages
// other than the build cache.
base.Fatalf("go: cannot use modules with build cache disabled")
}
}
// Failed reports whether module loading failed.
// If Failed returns true, then any use of module information will call die.
func Failed() bool {
Init()
return cfg.ModulesEnabled && ModRoot == ""
}
func die() {
if os.Getenv("GO111MODULE") == "off" {
base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
}
if inGOPATH && !MustUseModules {
base.Fatalf("go: modules disabled inside GOPATH/src by GO111MODULE=auto; see 'go help modules'")
}
base.Fatalf("go: cannot find main module; see 'go help modules'")
}
func InitMod() {
MustInit()
if modFile != nil {
return
}
list := filepath.SplitList(cfg.BuildContext.GOPATH)
if len(list) == 0 || list[0] == "" {
@ -255,9 +208,117 @@ func InitMod() {
}
modfetch.PkgMod = pkgMod
modfetch.GoSumFile = filepath.Join(ModRoot, "go.sum")
codehost.WorkRoot = filepath.Join(pkgMod, "cache/vcs")
cfg.ModulesEnabled = true
load.ModBinDir = BinDir
load.ModLookup = Lookup
load.ModPackageModuleInfo = PackageModuleInfo
load.ModImportPaths = ImportPaths
load.ModPackageBuildInfo = PackageBuildInfo
load.ModInfoProg = ModInfoProg
load.ModImportFromFiles = ImportFromFiles
load.ModDirImportPath = DirImportPath
if modRoot == "" {
// We're in module mode, but not inside a module.
//
// If the command is 'go get' or 'go list' and all of the args are in the
// same existing module, we could use that module's download directory in
// the module cache as the module root, applying any replacements and/or
// exclusions specified by that module. However, that would leave us in a
// strange state: we want 'go get' to be consistent with 'go list', and 'go
// list' should be able to operate on multiple modules. Moreover, the 'get'
// target might specify relative file paths (e.g. in the same repository) as
// replacements, and we would not be able to apply those anyway: we would
// need to either error out or ignore just those replacements, when a build
// from an empty module could proceed without error.
//
// Instead, we'll operate as though we're in some ephemeral external module,
// ignoring all replacements and exclusions uniformly.
// Normally we check sums using the go.sum file from the main module, but
// without a main module we do not have an authoritative go.sum file.
//
// TODO(bcmills): In Go 1.13, check sums when outside the main module.
//
// One possible approach is to merge the go.sum files from all of the
// modules we download: that doesn't protect us against bad top-level
// modules, but it at least ensures consistency for transitive dependencies.
} else {
modfetch.GoSumFile = filepath.Join(modRoot, "go.sum")
search.SetModRoot(modRoot)
}
}
func init() {
load.ModInit = Init
// Set modfetch.PkgMod unconditionally, so that go clean -modcache 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")
}
}
// Enabled reports whether modules are (or must be) enabled.
// If modules are enabled but there is no main module, Enabled returns true
// and then the first use of module information will call die
// (usually through MustModRoot).
func Enabled() bool {
Init()
return modRoot != "" || MustUseModules
}
// ModRoot returns the root of the main module.
// It calls base.Fatalf if there is no main module.
func ModRoot() string {
if !HasModRoot() {
die()
}
return modRoot
}
// HasModRoot reports whether a main module is present.
// HasModRoot may return false even if Enabled returns true: for example, 'get'
// does not require a main module.
func HasModRoot() bool {
Init()
return modRoot != ""
}
// printStackInDie causes die to print a stack trace.
//
// It is enabled by the testgo tag, and helps to diagnose paths that
// unexpectedly require a main module.
var printStackInDie = false
func die() {
if printStackInDie {
debug.PrintStack()
}
if os.Getenv("GO111MODULE") == "off" {
base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
}
if inGOPATH && !MustUseModules {
base.Fatalf("go: modules disabled inside GOPATH/src by GO111MODULE=auto; see 'go help modules'")
}
base.Fatalf("go: cannot find main module; see 'go help modules'")
}
// InitMod sets Target and, if there is a main module, parses the initial build
// list from its go.mod file, creating and populating that file if needed.
func InitMod() {
if len(buildList) > 0 {
return
}
Init()
if modRoot == "" {
Target = module.Version{Path: "command-line-arguments"}
buildList = []module.Version{Target}
return
}
if CmdModInit {
// Running go mod init: do legacy module conversion
legacyModInit()
@ -266,7 +327,7 @@ func InitMod() {
return
}
gomod := filepath.Join(ModRoot, "go.mod")
gomod := filepath.Join(modRoot, "go.mod")
data, err := ioutil.ReadFile(gomod)
if err != nil {
if os.IsNotExist(err) {
@ -284,10 +345,11 @@ func InitMod() {
base.Fatalf("go: errors parsing go.mod:\n%s\n", err)
}
modFile = f
modFileData = data
if len(f.Syntax.Stmt) == 0 || f.Module == nil {
// Empty mod file. Must add module path.
path, err := FindModulePath(ModRoot)
path, err := FindModulePath(modRoot)
if err != nil {
base.Fatalf("go: %v", err)
}
@ -325,7 +387,7 @@ func Allowed(m module.Version) bool {
func legacyModInit() {
if modFile == nil {
path, err := FindModulePath(ModRoot)
path, err := FindModulePath(modRoot)
if err != nil {
base.Fatalf("go: %v", err)
}
@ -334,8 +396,10 @@ func legacyModInit() {
modFile.AddModuleStmt(path)
}
addGoStmt()
for _, name := range altConfigs {
cfg := filepath.Join(ModRoot, name)
cfg := filepath.Join(modRoot, name)
data, err := ioutil.ReadFile(cfg)
if err == nil {
convert := modconv.Converters[name]
@ -356,6 +420,25 @@ func legacyModInit() {
}
}
// InitGoStmt adds a go statement, unless there already is one.
func InitGoStmt() {
if modFile.Go == nil {
addGoStmt()
}
}
// addGoStmt adds a go statement referring to the current version.
func addGoStmt() {
tags := build.Default.ReleaseTags
version := tags[len(tags)-1]
if !strings.HasPrefix(version, "go") || !modfile.GoVersionRE.MatchString(version[2:]) {
base.Fatalf("go: unrecognized default version %q", version)
}
if err := modFile.AddGoStmt(version[2:]); err != nil {
base.Fatalf("go: internal error: %v", err)
}
}
var altConfigs = []string{
"Gopkg.lock",
@ -379,7 +462,7 @@ func FindModuleRoot(dir, limit string, legacyConfigOK bool) (root, file string)
// Look for enclosing go.mod.
for {
if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() {
return dir, "go.mod"
}
if dir == limit {
@ -397,7 +480,7 @@ func FindModuleRoot(dir, limit string, legacyConfigOK bool) (root, file string)
dir = dir1
for {
for _, name := range altConfigs {
if _, err := os.Stat(filepath.Join(dir, name)); err == nil {
if fi, err := os.Stat(filepath.Join(dir, name)); err == nil && !fi.IsDir() {
return dir, name
}
}
@ -541,6 +624,11 @@ func WriteGoMod() {
return
}
// If we aren't in a module, we don't have anywhere to write a go.mod file.
if modRoot == "" {
return
}
if loaded != nil {
reqs := MinReqs()
min, err := reqs.Required(Target)
@ -557,22 +645,53 @@ func WriteGoMod() {
modFile.SetRequire(list)
}
file := filepath.Join(ModRoot, "go.mod")
old, _ := ioutil.ReadFile(file)
modFile.Cleanup() // clean file after edits
new, err := modFile.Format()
if err != nil {
base.Fatalf("go: %v", err)
}
if !bytes.Equal(old, new) {
if cfg.BuildMod == "readonly" {
base.Fatalf("go: updates to go.mod needed, disabled by -mod=readonly")
}
if err := ioutil.WriteFile(file, new, 0666); err != nil {
base.Fatalf("go: %v", err)
}
}
// Always update go.sum, even if we didn't change go.mod: we may have
// downloaded modules that we didn't have before.
modfetch.WriteGoSum()
if bytes.Equal(new, modFileData) {
// We don't need to modify go.mod from what we read previously.
// Ignore any intervening edits.
return
}
if cfg.BuildMod == "readonly" {
base.Fatalf("go: updates to go.mod needed, disabled by -mod=readonly")
}
unlock := modfetch.SideLock()
defer unlock()
file := filepath.Join(modRoot, "go.mod")
old, err := ioutil.ReadFile(file)
if !bytes.Equal(old, modFileData) {
if bytes.Equal(old, new) {
// Some other process wrote the same go.mod file that we were about to write.
modFileData = new
return
}
if err != nil {
base.Fatalf("go: can't determine whether go.mod has changed: %v", err)
}
// The contents of the go.mod file have changed. In theory we could add all
// of the new modules to the build list, recompute, and check whether any
// module in *our* build list got bumped to a different version, but that's
// a lot of work for marginal benefit. Instead, fail the command: if users
// want to run concurrent commands, they need to start with a complete,
// consistent module definition.
base.Fatalf("go: updates to go.mod needed, but contents have changed")
}
if err := renameio.WriteFile(file, new); err != nil {
base.Fatalf("error writing go.mod: %v", err)
}
modFileData = new
}
func fixVersion(path, vers string) (string, error) {

View File

@ -0,0 +1,42 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package modload
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
)
func TestFindModuleRootIgnoreDir(t *testing.T) {
// In Plan 9, directories are automatically created in /n.
// For example, /n/go.mod always exist, but it's a directory.
// Test that we ignore directories when trying to find go.mod and other config files.
dir, err := ioutil.TempDir("", "gotest")
if err != nil {
t.Fatalf("failed to create temporary directory: %v", err)
}
defer os.RemoveAll(dir)
if err := os.Mkdir(filepath.Join(dir, "go.mod"), os.ModeDir|0755); err != nil {
t.Fatalf("Mkdir failed: %v", err)
}
for _, name := range altConfigs {
if err := os.MkdirAll(filepath.Join(dir, name), os.ModeDir|0755); err != nil {
t.Fatalf("MkdirAll failed: %v", err)
}
}
p := filepath.Join(dir, "example")
if err := os.Mkdir(p, os.ModeDir|0755); err != nil {
t.Fatalf("Mkdir failed: %v", err)
}
if root, _ := FindModuleRoot(p, "", false); root != "" {
t.Errorf("FindModuleRoot(%q, \"\", false): %q, want empty string", p, root)
}
if root, _ := FindModuleRoot(p, "", true); root != "" {
t.Errorf("FindModuleRoot(%q, \"\", true): %q, want empty string", p, root)
}
}

View File

@ -17,7 +17,7 @@ import (
)
func ListModules(args []string, listU, listVersions bool) []*modinfo.ModulePublic {
mods := listModules(args)
mods := listModules(args, listVersions)
if listU || listVersions {
var work par.Work
for _, m := range mods {
@ -39,7 +39,7 @@ func ListModules(args []string, listU, listVersions bool) []*modinfo.ModulePubli
return mods
}
func listModules(args []string) []*modinfo.ModulePublic {
func listModules(args []string, listVersions bool) []*modinfo.ModulePublic {
LoadBuildList()
if len(args) == 0 {
return []*modinfo.ModulePublic{moduleInfo(buildList[0], true)}
@ -83,6 +83,10 @@ func listModules(args []string) []*modinfo.ModulePublic {
}
matched := false
for i, m := range buildList {
if i == 0 && !HasModRoot() {
// The root module doesn't actually exist: omit it.
continue
}
if match(m.Path) {
matched = true
if !matchedBuildList[i] {
@ -93,6 +97,16 @@ func listModules(args []string) []*modinfo.ModulePublic {
}
if !matched {
if literal {
if listVersions {
// Don't make the user provide an explicit '@latest' when they're
// explicitly asking what the available versions are.
// Instead, resolve the module, even if it isn't an existing dependency.
info, err := Query(arg, "latest", nil)
if err == nil {
mods = append(mods, moduleInfo(module.Version{Path: arg, Version: info.Version}, false))
continue
}
}
mods = append(mods, &modinfo.ModulePublic{
Path: arg,
Error: &modinfo.ModuleError{

View File

@ -90,7 +90,7 @@ func ImportPaths(patterns []string) []*search.Match {
// the exact version of a particular module increases during
// the loader iterations.
m.Pkgs = str.StringList(fsDirs[i])
for i, pkg := range m.Pkgs {
for j, pkg := range m.Pkgs {
dir := pkg
if !filepath.IsAbs(dir) {
dir = filepath.Join(cwd, pkg)
@ -101,10 +101,10 @@ func ImportPaths(patterns []string) []*search.Match {
// 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 dir == ModRoot {
if modRoot != "" && dir == modRoot {
pkg = Target.Path
} else if strings.HasPrefix(dir, ModRoot+string(filepath.Separator)) && !strings.Contains(dir[len(ModRoot):], "@") {
suffix := filepath.ToSlash(dir[len(ModRoot):])
} 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/")
@ -118,24 +118,21 @@ func ImportPaths(patterns []string) []*search.Match {
} else {
pkg = ""
if !iterating {
ModRoot()
base.Errorf("go: directory %s outside available modules", base.ShortPath(dir))
}
}
info, err := os.Stat(dir)
if err != nil || !info.IsDir() {
// If the directory does not exist,
// don't turn it into an import path
// that will trigger a lookup.
pkg = ""
if !iterating {
if err != nil {
base.Errorf("go: no such directory %v", m.Pattern)
} else {
base.Errorf("go: %s is not a directory", m.Pattern)
}
// If the directory is local but does not exist, don't return it
// while loader is iterating, since this would 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 {
pkg = ""
}
}
m.Pkgs[i] = pkg
m.Pkgs[j] = pkg
}
case strings.Contains(m.Pattern, "..."):
@ -251,17 +248,21 @@ func ImportFromFiles(gofiles []string) {
// DirImportPath returns the effective import path for dir,
// provided it is within the main module, or else returns ".".
func DirImportPath(dir string) string {
if modRoot == "" {
return "."
}
if !filepath.IsAbs(dir) {
dir = filepath.Join(cwd, dir)
} else {
dir = filepath.Clean(dir)
}
if dir == ModRoot {
if dir == modRoot {
return Target.Path
}
if strings.HasPrefix(dir, ModRoot+string(filepath.Separator)) {
suffix := filepath.ToSlash(dir[len(ModRoot):])
if strings.HasPrefix(dir, modRoot+string(filepath.Separator)) {
suffix := filepath.ToSlash(dir[len(modRoot):])
if strings.HasPrefix(suffix, "/vendor/") {
return strings.TrimPrefix(suffix, "/vendor/")
}
@ -397,6 +398,9 @@ func ModuleUsedDirectly(path string) bool {
// Lookup requires that one of the Load functions in this package has already
// been called.
func Lookup(path string) (dir, realPath string, err error) {
if path == "" {
panic("Lookup called with empty package path")
}
pkg, ok := loaded.pkgCache.Get(path).(*loadPkg)
if !ok {
// The loader should have found all the relevant paths.
@ -762,7 +766,7 @@ func (pkg *loadPkg) stackText() string {
}
// why returns the text to use in "go mod why" output about the given package.
// It is less ornate than the stackText but conatins the same information.
// It is less ornate than the stackText but contains the same information.
func (pkg *loadPkg) why() string {
var buf strings.Builder
var stack []*loadPkg
@ -811,7 +815,7 @@ func WhyDepth(path string) int {
// a module.Version with Path == "".
func Replacement(mod module.Version) module.Version {
if modFile == nil {
// Happens during testing.
// Happens during testing and if invoking 'go get' or 'go list' outside a module.
return module.Version{}
}
@ -888,7 +892,7 @@ func readVendorList() {
vendorOnce.Do(func() {
vendorList = nil
vendorMap = make(map[string]module.Version)
data, _ := ioutil.ReadFile(filepath.Join(ModRoot, "vendor/modules.txt"))
data, _ := ioutil.ReadFile(filepath.Join(ModRoot(), "vendor/modules.txt"))
var m module.Version
for _, line := range strings.Split(string(data), "\n") {
if strings.HasPrefix(line, "# ") {
@ -918,7 +922,7 @@ func (r *mvsReqs) modFileToList(f *modfile.File) []module.Version {
func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
if mod == Target {
if modFile.Go != nil {
if modFile != nil && modFile.Go != nil {
r.versions.LoadOrStore(mod, modFile.Go.Version)
}
var list []module.Version
@ -938,7 +942,7 @@ func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
// TODO: need to slip the new version into the tags list etc.
dir := repl.Path
if !filepath.IsAbs(dir) {
dir = filepath.Join(ModRoot, dir)
dir = filepath.Join(ModRoot(), dir)
}
gomod := filepath.Join(dir, "go.mod")
data, err := ioutil.ReadFile(gomod)
@ -1053,13 +1057,13 @@ func (*mvsReqs) next(m module.Version) (module.Version, error) {
func fetch(mod module.Version) (dir string, isLocal bool, err error) {
if mod == Target {
return ModRoot, true, nil
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)
dir = filepath.Join(ModRoot(), dir)
}
return dir, true, nil
}

View File

@ -207,21 +207,23 @@ func matchSemverPrefix(p, v string) bool {
// If multiple modules with revisions matching the query provide the requested
// package, QueryPackage picks the one with the longest module path.
//
// If the path is in the the main module and the query is "latest",
// If the path is in the main module and the query is "latest",
// QueryPackage returns Target as the version.
func QueryPackage(path, query string, allowed func(module.Version) bool) (module.Version, *modfetch.RevInfo, error) {
if _, ok := dirInModule(path, Target.Path, ModRoot, true); ok {
if query != "latest" {
return module.Version{}, nil, fmt.Errorf("can't query specific version (%q) for package %s in the main module (%s)", query, path, Target.Path)
if HasModRoot() {
if _, ok := dirInModule(path, Target.Path, modRoot, true); ok {
if query != "latest" {
return module.Version{}, nil, fmt.Errorf("can't query specific version (%q) for package %s in the main module (%s)", query, path, Target.Path)
}
if !allowed(Target) {
return module.Version{}, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed", path, Target.Path)
}
return Target, &modfetch.RevInfo{Version: Target.Version}, nil
}
if !allowed(Target) {
return module.Version{}, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed", path, Target.Path)
}
return Target, &modfetch.RevInfo{Version: Target.Version}, nil
}
finalErr := errMissing
for p := path; p != "."; p = pathpkg.Dir(p) {
for p := path; p != "." && p != "/"; p = pathpkg.Dir(p) {
info, err := Query(p, query, allowed)
if err != nil {
if _, ok := err.(*codehost.VCSError); ok {

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