mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-03-24 16:30:53 +08:00
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:
parent
225220d668
commit
4f4a855d82
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1 +1 @@
|
||||
go1.11.1
|
||||
go1.12beta2
|
||||
|
@ -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
4
libgo/configure
vendored
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 }
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
@ -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")
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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).
|
||||
|
@ -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))
|
||||
}
|
||||
|
28
libgo/go/cmd/go/help_test.go
Normal file
28
libgo/go/cmd/go/help_test.go
Normal 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")
|
||||
}
|
||||
}
|
8
libgo/go/cmd/go/internal/cache/cache.go
vendored
8
libgo/go/cmd/go/internal/cache/cache.go
vendored
@ -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
|
||||
}
|
||||
|
113
libgo/go/cmd/go/internal/cache/default.go
vendored
113
libgo/go/cmd/go/internal/cache/default.go
vendored
@ -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
|
||||
}
|
||||
|
@ -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 /")
|
||||
}
|
||||
}
|
2
libgo/go/cmd/go/internal/cache/hash.go
vendored
2
libgo/go/cmd/go/internal/cache/hash.go
vendored
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
192
libgo/go/cmd/go/internal/get/path.go
Normal file
192
libgo/go/cmd/go/internal/get/path.go
Normal 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",
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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 "
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
122
libgo/go/cmd/go/internal/lockedfile/lockedfile.go
Normal file
122
libgo/go/cmd/go/internal/lockedfile/lockedfile.go
Normal 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
|
||||
}
|
64
libgo/go/cmd/go/internal/lockedfile/lockedfile_filelock.go
Normal file
64
libgo/go/cmd/go/internal/lockedfile/lockedfile_filelock.go
Normal 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
|
||||
}
|
93
libgo/go/cmd/go/internal/lockedfile/lockedfile_plan9.go
Normal file
93
libgo/go/cmd/go/internal/lockedfile/lockedfile_plan9.go
Normal 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()
|
||||
}
|
174
libgo/go/cmd/go/internal/lockedfile/lockedfile_test.go
Normal file
174
libgo/go/cmd/go/internal/lockedfile/lockedfile_test.go
Normal 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)
|
||||
}
|
60
libgo/go/cmd/go/internal/lockedfile/mutex.go
Normal file
60
libgo/go/cmd/go/internal/lockedfile/mutex.go
Normal 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
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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})
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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] {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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", "/")
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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)))
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
42
libgo/go/cmd/go/internal/modload/init_test.go
Normal file
42
libgo/go/cmd/go/internal/modload/init_test.go
Normal 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)
|
||||
}
|
||||
}
|
@ -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{
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user