libgo: update to Go 1.11
Reviewed-on: https://go-review.googlesource.com/136435 gotools/: * Makefile.am (mostlyclean-local): Run chmod on check-go-dir to make sure it is writable. (check-go-tools): Likewise. (check-vet): Copy internal/objabi to check-vet-dir. * Makefile.in: Rebuild. From-SVN: r264546
This commit is contained in:
parent
779d8a5ad0
commit
dd931d9b48
1516 changed files with 83937 additions and 19003 deletions
|
@ -1,4 +1,4 @@
|
|||
92a14213215fd93df7240fa9d376a1213b1d5a74
|
||||
7b25b4dff4778fc4d6b5d6e10594814146b3e5dd
|
||||
|
||||
The first line of this file holds the git revision number of the last
|
||||
merge done from the gofrontend repository.
|
||||
|
|
|
@ -8,13 +8,15 @@
|
|||
|
||||
package main
|
||||
|
||||
import "unsafe"
|
||||
|
||||
var bug = false
|
||||
|
||||
var minus1 = -1
|
||||
var five = 5
|
||||
var big int64 = 10 | 1<<32
|
||||
var big int64 = 10 | 1<<40
|
||||
|
||||
type block [1<<19]byte
|
||||
type block [1 << 19]byte
|
||||
|
||||
var g1 []block
|
||||
|
||||
|
@ -48,9 +50,10 @@ func bigcap() {
|
|||
g1 = make([]block, 10, big)
|
||||
}
|
||||
|
||||
type cblock [1<<16-1]byte
|
||||
type cblock [1<<16 - 1]byte
|
||||
|
||||
var g4 chan cblock
|
||||
|
||||
func badchancap() {
|
||||
g4 = make(chan cblock, minus1)
|
||||
}
|
||||
|
@ -60,7 +63,8 @@ func bigchancap() {
|
|||
}
|
||||
|
||||
func overflowchan() {
|
||||
g4 = make(chan cblock, 1<<30)
|
||||
const ptrSize = unsafe.Sizeof(uintptr(0))
|
||||
g4 = make(chan cblock, 1<<(30*(ptrSize/4)))
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// run
|
||||
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
|
@ -15,22 +15,31 @@ type T []int
|
|||
|
||||
func main() {
|
||||
n := -1
|
||||
shouldPanic("len out of range", func() {_ = make(T, n)})
|
||||
shouldPanic("cap out of range", func() {_ = make(T, 0, n)})
|
||||
shouldPanic("len out of range", func() { _ = make(T, n) })
|
||||
shouldPanic("cap out of range", func() { _ = make(T, 0, n) })
|
||||
shouldPanic("len out of range", func() { _ = make(T, int64(n)) })
|
||||
shouldPanic("cap out of range", func() { _ = make(T, 0, int64(n)) })
|
||||
var t *byte
|
||||
if unsafe.Sizeof(t) == 8 {
|
||||
n = 1<<20
|
||||
n <<= 20
|
||||
shouldPanic("len out of range", func() {_ = make(T, n)})
|
||||
shouldPanic("cap out of range", func() {_ = make(T, 0, n)})
|
||||
n <<= 20
|
||||
shouldPanic("len out of range", func() {_ = make(T, n)})
|
||||
shouldPanic("cap out of range", func() {_ = make(T, 0, n)})
|
||||
var n2 int64 = 1 << 50
|
||||
shouldPanic("len out of range", func() { _ = make(T, int(n2)) })
|
||||
shouldPanic("cap out of range", func() { _ = make(T, 0, int(n2)) })
|
||||
n2 = 1<<63 - 1
|
||||
shouldPanic("len out of range", func() { _ = make(T, int(n2)) })
|
||||
shouldPanic("cap out of range", func() { _ = make(T, 0, int(n2)) })
|
||||
} else {
|
||||
n = 1<<31 - 1
|
||||
shouldPanic("len out of range", func() {_ = make(T, n)})
|
||||
shouldPanic("cap out of range", func() {_ = make(T, 0, n)})
|
||||
shouldPanic("len out of range", func() { _ = make(T, n) })
|
||||
shouldPanic("cap out of range", func() { _ = make(T, 0, n) })
|
||||
shouldPanic("len out of range", func() { _ = make(T, int64(n)) })
|
||||
shouldPanic("cap out of range", func() { _ = make(T, 0, int64(n)) })
|
||||
}
|
||||
|
||||
// Test make in append panics since the gc compiler optimizes makes in appends.
|
||||
shouldPanic("len out of range", func() { _ = append(T{}, make(T, n)...) })
|
||||
shouldPanic("cap out of range", func() { _ = append(T{}, make(T, 0, n)...) })
|
||||
shouldPanic("len out of range", func() { _ = append(T{}, make(T, int64(n))...) })
|
||||
shouldPanic("cap out of range", func() { _ = append(T{}, make(T, 0, int64(n))...) })
|
||||
}
|
||||
|
||||
func shouldPanic(str string, f func()) {
|
||||
|
@ -44,6 +53,6 @@ func shouldPanic(str string, f func()) {
|
|||
panic("got panic " + s + ", want " + str)
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
f()
|
||||
}
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
2018-09-24 Ian Lance Taylor <iant@golang.org>
|
||||
|
||||
* Makefile.am (mostlyclean-local): Run chmod on check-go-dir to
|
||||
make sure it is writable.
|
||||
(check-go-tools): Likewise.
|
||||
(check-vet): Copy internal/objabi to check-vet-dir.
|
||||
* Makefile.in: Rebuild.
|
||||
|
||||
2018-05-09 Ian Lance Taylor <iant@golang.org>
|
||||
|
||||
* Makefile.am (check-go-tool): Don't copy zstdpkglist.go.
|
||||
|
|
|
@ -123,6 +123,7 @@ MOSTLYCLEANFILES = \
|
|||
*.sent
|
||||
|
||||
mostlyclean-local:
|
||||
chmod -R u+w check-go-dir
|
||||
rm -rf check-go-dir check-runtime-dir cgo-test-dir carchive-test-dir
|
||||
|
||||
if NATIVE
|
||||
|
@ -228,6 +229,7 @@ ECHO_ENV = PATH=`echo $(abs_builddir):$${PATH} | sed 's,::*,:,g;s,^:*,,;s,:*$$,,
|
|||
|
||||
# check-go-tool runs `go test cmd/go` in our environment.
|
||||
check-go-tool: go$(EXEEXT) $(noinst_PROGRAMS) check-head check-gccgo check-gcc
|
||||
chmod -R u+w check-go-dir
|
||||
rm -rf check-go-dir cmd_go-testlog
|
||||
$(MKDIR_P) check-go-dir/src/cmd/go
|
||||
cp $(cmdsrcdir)/go/*.go check-go-dir/src/cmd/go/
|
||||
|
@ -297,8 +299,10 @@ check-carchive-test: go$(EXEEXT) $(noinst_PROGRAMS) check-head check-gccgo check
|
|||
# check-vet runs `go test cmd/vet` in our environment.
|
||||
check-vet: go$(EXEEXT) $(noinst_PROGRAMS) check-head check-gccgo check-gcc
|
||||
rm -rf check-vet-dir cmd_vet-testlog
|
||||
$(MKDIR_P) check-vet-dir/src/cmd
|
||||
$(MKDIR_P) check-vet-dir/src/cmd/internal
|
||||
cp -r $(cmdsrcdir)/vet check-vet-dir/src/cmd/
|
||||
cp -r $(cmdsrcdir)/internal/objabi check-vet-dir/src/cmd/internal
|
||||
cp $(libgodir)/objabi.go check-vet-dir/src/cmd/internal/objabi/
|
||||
@abs_libgodir=`cd $(libgodir) && $(PWD_COMMAND)`; \
|
||||
abs_checkdir=`cd check-vet-dir && $(PWD_COMMAND)`; \
|
||||
echo "cd check-vet-dir/src/cmd/vet && $(ECHO_ENV) GOPATH=$${abs_checkdir} $(abs_builddir)/go$(EXEEXT) test -test.short -test.timeout=$(GOTOOLS_TEST_TIMEOUT)s -test.v" > cmd_vet-testlog
|
||||
|
|
|
@ -637,8 +637,8 @@ distclean-generic:
|
|||
maintainer-clean-generic:
|
||||
@echo "This command is intended for maintainers to use"
|
||||
@echo "it deletes files that may require special tools to rebuild."
|
||||
@NATIVE_FALSE@uninstall-local:
|
||||
@NATIVE_FALSE@install-exec-local:
|
||||
@NATIVE_FALSE@uninstall-local:
|
||||
clean: clean-am
|
||||
|
||||
clean-am: clean-binPROGRAMS clean-generic clean-noinstPROGRAMS \
|
||||
|
@ -744,6 +744,7 @@ s-zdefaultcc: Makefile
|
|||
$(STAMP) $@
|
||||
|
||||
mostlyclean-local:
|
||||
chmod -R u+w check-go-dir
|
||||
rm -rf check-go-dir check-runtime-dir cgo-test-dir carchive-test-dir
|
||||
|
||||
@NATIVE_TRUE@go$(EXEEXT): $(go_cmd_go_files) $(LIBGOTOOL) $(LIBGODEP)
|
||||
|
@ -807,6 +808,7 @@ mostlyclean-local:
|
|||
|
||||
# check-go-tool runs `go test cmd/go` in our environment.
|
||||
@NATIVE_TRUE@check-go-tool: go$(EXEEXT) $(noinst_PROGRAMS) check-head check-gccgo check-gcc
|
||||
@NATIVE_TRUE@ chmod -R u+w check-go-dir
|
||||
@NATIVE_TRUE@ rm -rf check-go-dir cmd_go-testlog
|
||||
@NATIVE_TRUE@ $(MKDIR_P) check-go-dir/src/cmd/go
|
||||
@NATIVE_TRUE@ cp $(cmdsrcdir)/go/*.go check-go-dir/src/cmd/go/
|
||||
|
@ -876,8 +878,10 @@ mostlyclean-local:
|
|||
# check-vet runs `go test cmd/vet` in our environment.
|
||||
@NATIVE_TRUE@check-vet: go$(EXEEXT) $(noinst_PROGRAMS) check-head check-gccgo check-gcc
|
||||
@NATIVE_TRUE@ rm -rf check-vet-dir cmd_vet-testlog
|
||||
@NATIVE_TRUE@ $(MKDIR_P) check-vet-dir/src/cmd
|
||||
@NATIVE_TRUE@ $(MKDIR_P) check-vet-dir/src/cmd/internal
|
||||
@NATIVE_TRUE@ cp -r $(cmdsrcdir)/vet check-vet-dir/src/cmd/
|
||||
@NATIVE_TRUE@ cp -r $(cmdsrcdir)/internal/objabi check-vet-dir/src/cmd/internal
|
||||
@NATIVE_TRUE@ cp $(libgodir)/objabi.go check-vet-dir/src/cmd/internal/objabi/
|
||||
@NATIVE_TRUE@ @abs_libgodir=`cd $(libgodir) && $(PWD_COMMAND)`; \
|
||||
@NATIVE_TRUE@ abs_checkdir=`cd check-vet-dir && $(PWD_COMMAND)`; \
|
||||
@NATIVE_TRUE@ echo "cd check-vet-dir/src/cmd/vet && $(ECHO_ENV) GOPATH=$${abs_checkdir} $(abs_builddir)/go$(EXEEXT) test -test.short -test.timeout=$(GOTOOLS_TEST_TIMEOUT)s -test.v" > cmd_vet-testlog
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
fe8a0d12b14108cbe2408b417afcaab722b0727c
|
||||
41e62b8c49d21659b48a95216e3062032285250f
|
||||
|
||||
The first line of this file holds the git revision number of the
|
||||
last merge done from the master library sources.
|
||||
|
|
|
@ -541,6 +541,7 @@ s-objabi: Makefile
|
|||
echo 'const defaultGO386 = `sse2`' >> objabi.go.tmp
|
||||
echo 'const defaultGOARM = `5`' >> objabi.go.tmp
|
||||
echo 'const defaultGOMIPS = `hardfloat`' >> objabi.go.tmp
|
||||
echo 'const defaultGOMIPS64 = `hardfloat`' >> objabi.go.tmp
|
||||
echo 'const defaultGOOS = runtime.GOOS' >> objabi.go.tmp
|
||||
echo 'const defaultGOARCH = runtime.GOARCH' >> objabi.go.tmp
|
||||
echo 'const defaultGO_EXTLINK_ENABLED = ``' >> objabi.go.tmp
|
||||
|
@ -608,7 +609,7 @@ noinst_DATA += zdefaultcc.go
|
|||
zstdpkglist.go: s-zstdpkglist; @true
|
||||
s-zstdpkglist: Makefile
|
||||
rm -f zstdpkglist.go.tmp
|
||||
echo 'package build' > zstdpkglist.go.tmp
|
||||
echo 'package goroot' > zstdpkglist.go.tmp
|
||||
echo "" >> zstdpkglist.go.tmp
|
||||
echo 'var stdpkg = map[string]bool{' >> zstdpkglist.go.tmp
|
||||
echo $(libgo_go_objs) 'unsafe.lo' 'runtime/cgo.lo' | sed 's|[a-z0-9_/]*_c\.lo||g' | sed 's|\([a-z0-9_/]*\)\.lo|"\1": true,|g' >> zstdpkglist.go.tmp
|
||||
|
@ -707,6 +708,7 @@ PACKAGES = $(shell cat $(srcdir)/libgo-packages.txt)
|
|||
libgo_go_objs = \
|
||||
$(addsuffix .lo,$(PACKAGES)) \
|
||||
bytes/index.lo \
|
||||
internal/bytealg/bytealg.lo \
|
||||
reflect/makefunc_ffi_c.lo \
|
||||
strings/index.lo \
|
||||
$(syscall_lib_clone_lo) \
|
||||
|
@ -718,7 +720,8 @@ libgo_go_objs = \
|
|||
log/syslog/syslog_c.lo \
|
||||
$(os_lib_inotify_lo) \
|
||||
runtime/internal/atomic_c.lo \
|
||||
sync/atomic_c.lo
|
||||
sync/atomic_c.lo \
|
||||
internal/cpu/cpu_gccgo.lo
|
||||
|
||||
libgo_ldflags = \
|
||||
-version-info $(libtool_VERSION) $(PTHREAD_CFLAGS) $(AM_LDFLAGS)
|
||||
|
@ -960,8 +963,8 @@ runtime_pprof_check_GOCFLAGS = -static-libgo -fno-inline
|
|||
extra_go_files_runtime_internal_sys = version.go
|
||||
runtime/internal/sys.lo.dep: $(extra_go_files_runtime_internal_sys)
|
||||
|
||||
extra_go_files_go_build = zstdpkglist.go
|
||||
go/build.lo.dep: $(extra_go_files_go_build)
|
||||
extra_go_files_internal_goroot = zstdpkglist.go
|
||||
internal/goroot.lo.dep: $(extra_go_files_internal_goroot)
|
||||
|
||||
extra_go_files_go_types = gccgosizes.go
|
||||
go/types.lo.dep: $(extra_go_files_go_types)
|
||||
|
@ -976,6 +979,16 @@ extra_check_libs_cmd_go_internal_cache = $(abs_builddir)/libgotool.a
|
|||
extra_check_libs_cmd_go_internal_generate = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_get = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_load = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_imports = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_modconv = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_modfetch = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_modfetch_codehost = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_modfile = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_modload = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_module = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_mvs = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_search = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_web2 = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_work = $(abs_builddir)/libgotool.a
|
||||
|
||||
extra_check_libs_cmd_vet_internal_cfg = $(abs_builddir)/libgotool.a
|
||||
|
@ -990,6 +1003,9 @@ bytes/index.lo: go/bytes/indexbyte.c runtime.inc
|
|||
strings/index.lo: go/strings/indexbyte.c runtime.inc
|
||||
@$(MKDIR_P) strings
|
||||
$(LTCOMPILE) -c -o $@ $(srcdir)/go/strings/indexbyte.c
|
||||
internal/bytealg/bytealg.lo: go/internal/bytealg/bytealg.c runtime.inc
|
||||
@$(MKDIR_P) internal/bytealg
|
||||
$(LTCOMPILE) -c -o $@ $(srcdir)/go/internal/bytealg/bytealg.c
|
||||
|
||||
# Use a C function with a fixed number of arguments to call a C
|
||||
# varargs function.
|
||||
|
@ -1024,6 +1040,11 @@ syscall/wait.lo: go/syscall/wait.c runtime.inc
|
|||
@$(MKDIR_P) syscall
|
||||
$(LTCOMPILE) -c -o $@ $(srcdir)/go/syscall/wait.c
|
||||
|
||||
# internal/cpu needs some C code.
|
||||
internal/cpu/cpu_gccgo.lo: go/internal/cpu/cpu_gccgo.c runtime.inc
|
||||
@$(MKDIR_P) internal/cpu
|
||||
$(LTCOMPILE) -c -o $@ $(srcdir)/go/internal/cpu/cpu_gccgo.c
|
||||
|
||||
# Solaris 11.4 changed the type of fields in struct stat.
|
||||
# Use a build tag, based on a configure check, to cope.
|
||||
if LIBGO_IS_SOLARIS
|
||||
|
|
|
@ -174,11 +174,12 @@ libgotool_a_OBJECTS = $(am_libgotool_a_OBJECTS)
|
|||
LTLIBRARIES = $(toolexeclib_LTLIBRARIES)
|
||||
@LIBGO_IS_LINUX_TRUE@am__DEPENDENCIES_1 = syscall/clone_linux.lo
|
||||
am__DEPENDENCIES_2 = $(addsuffix .lo,$(PACKAGES)) bytes/index.lo \
|
||||
reflect/makefunc_ffi_c.lo strings/index.lo \
|
||||
$(am__DEPENDENCIES_1) syscall/errno.lo syscall/signame.lo \
|
||||
syscall/wait.lo $(golang_org_x_net_lif_lo) \
|
||||
internal/bytealg/bytealg.lo reflect/makefunc_ffi_c.lo \
|
||||
strings/index.lo $(am__DEPENDENCIES_1) syscall/errno.lo \
|
||||
syscall/signame.lo syscall/wait.lo $(golang_org_x_net_lif_lo) \
|
||||
$(golang_org_x_net_route_lo) log/syslog/syslog_c.lo \
|
||||
runtime/internal/atomic_c.lo sync/atomic_c.lo
|
||||
runtime/internal/atomic_c.lo sync/atomic_c.lo \
|
||||
internal/cpu/cpu_gccgo.lo
|
||||
am__DEPENDENCIES_3 =
|
||||
am__DEPENDENCIES_4 = $(am__DEPENDENCIES_2) \
|
||||
../libbacktrace/libbacktrace.la $(am__DEPENDENCIES_3) \
|
||||
|
@ -824,6 +825,7 @@ PACKAGES = $(shell cat $(srcdir)/libgo-packages.txt)
|
|||
libgo_go_objs = \
|
||||
$(addsuffix .lo,$(PACKAGES)) \
|
||||
bytes/index.lo \
|
||||
internal/bytealg/bytealg.lo \
|
||||
reflect/makefunc_ffi_c.lo \
|
||||
strings/index.lo \
|
||||
$(syscall_lib_clone_lo) \
|
||||
|
@ -835,7 +837,8 @@ libgo_go_objs = \
|
|||
log/syslog/syslog_c.lo \
|
||||
$(os_lib_inotify_lo) \
|
||||
runtime/internal/atomic_c.lo \
|
||||
sync/atomic_c.lo
|
||||
sync/atomic_c.lo \
|
||||
internal/cpu/cpu_gccgo.lo
|
||||
|
||||
libgo_ldflags = \
|
||||
-version-info $(libtool_VERSION) $(PTHREAD_CFLAGS) $(AM_LDFLAGS)
|
||||
|
@ -999,7 +1002,7 @@ runtime_internal_sys_lo_check_GOCFLAGS = -fgo-compiling-runtime
|
|||
# Also use -fno-inline to get better results from the memory profiler.
|
||||
runtime_pprof_check_GOCFLAGS = -static-libgo -fno-inline
|
||||
extra_go_files_runtime_internal_sys = version.go
|
||||
extra_go_files_go_build = zstdpkglist.go
|
||||
extra_go_files_internal_goroot = zstdpkglist.go
|
||||
extra_go_files_go_types = gccgosizes.go
|
||||
extra_go_files_cmd_internal_objabi = objabi.go
|
||||
extra_go_files_cmd_go_internal_cfg = zdefaultcc.go
|
||||
|
@ -1007,6 +1010,16 @@ extra_check_libs_cmd_go_internal_cache = $(abs_builddir)/libgotool.a
|
|||
extra_check_libs_cmd_go_internal_generate = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_get = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_load = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_imports = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_modconv = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_modfetch = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_modfetch_codehost = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_modfile = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_modload = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_module = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_mvs = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_search = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_web2 = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_go_internal_work = $(abs_builddir)/libgotool.a
|
||||
extra_check_libs_cmd_vet_internal_cfg = $(abs_builddir)/libgotool.a
|
||||
@HAVE_STAT_TIMESPEC_FALSE@@LIBGO_IS_SOLARIS_TRUE@matchargs_os =
|
||||
|
@ -2755,6 +2768,7 @@ s-objabi: Makefile
|
|||
echo 'const defaultGO386 = `sse2`' >> objabi.go.tmp
|
||||
echo 'const defaultGOARM = `5`' >> objabi.go.tmp
|
||||
echo 'const defaultGOMIPS = `hardfloat`' >> objabi.go.tmp
|
||||
echo 'const defaultGOMIPS64 = `hardfloat`' >> objabi.go.tmp
|
||||
echo 'const defaultGOOS = runtime.GOOS' >> objabi.go.tmp
|
||||
echo 'const defaultGOARCH = runtime.GOARCH' >> objabi.go.tmp
|
||||
echo 'const defaultGO_EXTLINK_ENABLED = ``' >> objabi.go.tmp
|
||||
|
@ -2816,7 +2830,7 @@ s-runtime-inc: runtime.lo Makefile
|
|||
zstdpkglist.go: s-zstdpkglist; @true
|
||||
s-zstdpkglist: Makefile
|
||||
rm -f zstdpkglist.go.tmp
|
||||
echo 'package build' > zstdpkglist.go.tmp
|
||||
echo 'package goroot' > zstdpkglist.go.tmp
|
||||
echo "" >> zstdpkglist.go.tmp
|
||||
echo 'var stdpkg = map[string]bool{' >> zstdpkglist.go.tmp
|
||||
echo $(libgo_go_objs) 'unsafe.lo' 'runtime/cgo.lo' | sed 's|[a-z0-9_/]*_c\.lo||g' | sed 's|\([a-z0-9_/]*\)\.lo|"\1": true,|g' >> zstdpkglist.go.tmp
|
||||
|
@ -2943,7 +2957,7 @@ $(foreach package,$(GOTOOL_PACKAGES),$(eval $(call PACKAGE_template,$(package)))
|
|||
runtime.lo.dep: $(extra_go_files_runtime)
|
||||
syscall.lo.dep: $(extra_go_files_syscall)
|
||||
runtime/internal/sys.lo.dep: $(extra_go_files_runtime_internal_sys)
|
||||
go/build.lo.dep: $(extra_go_files_go_build)
|
||||
internal/goroot.lo.dep: $(extra_go_files_internal_goroot)
|
||||
go/types.lo.dep: $(extra_go_files_go_types)
|
||||
cmd/internal/objabi.lo.dep: $(extra_go_files_cmd_internal_objabi)
|
||||
cmd/go/internal/cfg.lo.dep: $(extra_go_files_cmd_go_internal_cfg)
|
||||
|
@ -2958,6 +2972,9 @@ bytes/index.lo: go/bytes/indexbyte.c runtime.inc
|
|||
strings/index.lo: go/strings/indexbyte.c runtime.inc
|
||||
@$(MKDIR_P) strings
|
||||
$(LTCOMPILE) -c -o $@ $(srcdir)/go/strings/indexbyte.c
|
||||
internal/bytealg/bytealg.lo: go/internal/bytealg/bytealg.c runtime.inc
|
||||
@$(MKDIR_P) internal/bytealg
|
||||
$(LTCOMPILE) -c -o $@ $(srcdir)/go/internal/bytealg/bytealg.c
|
||||
|
||||
# Use a C function with a fixed number of arguments to call a C
|
||||
# varargs function.
|
||||
|
@ -2992,6 +3009,11 @@ syscall/wait.lo: go/syscall/wait.c runtime.inc
|
|||
@$(MKDIR_P) syscall
|
||||
$(LTCOMPILE) -c -o $@ $(srcdir)/go/syscall/wait.c
|
||||
|
||||
# internal/cpu needs some C code.
|
||||
internal/cpu/cpu_gccgo.lo: go/internal/cpu/cpu_gccgo.c runtime.inc
|
||||
@$(MKDIR_P) internal/cpu
|
||||
$(LTCOMPILE) -c -o $@ $(srcdir)/go/internal/cpu/cpu_gccgo.c
|
||||
|
||||
# Build golang_org/x/net/route only on BSD systems.
|
||||
|
||||
@LIBGO_IS_BSD_TRUE@$(eval $(call PACKAGE_template,golang_org/x/net/route))
|
||||
|
|
|
@ -1 +1 @@
|
|||
go1.10.3
|
||||
go1.11
|
||||
|
|
|
@ -3,9 +3,23 @@ archive/zip
|
|||
bufio
|
||||
bytes
|
||||
cmd/go/internal/cache
|
||||
cmd/go/internal/dirhash
|
||||
cmd/go/internal/generate
|
||||
cmd/go/internal/get
|
||||
cmd/go/internal/imports
|
||||
cmd/go/internal/load
|
||||
cmd/go/internal/modconv
|
||||
cmd/go/internal/modfetch
|
||||
cmd/go/internal/modfetch/codehost
|
||||
cmd/go/internal/modfile
|
||||
cmd/go/internal/modload
|
||||
cmd/go/internal/module
|
||||
cmd/go/internal/mvs
|
||||
cmd/go/internal/par
|
||||
cmd/go/internal/search
|
||||
cmd/go/internal/semver
|
||||
cmd/go/internal/txtar
|
||||
cmd/go/internal/web2
|
||||
cmd/go/internal/work
|
||||
cmd/internal/buildid
|
||||
cmd/internal/edit
|
||||
|
@ -29,6 +43,7 @@ crypto/dsa
|
|||
crypto/ecdsa
|
||||
crypto/elliptic
|
||||
crypto/hmac
|
||||
crypto/internal/subtle
|
||||
crypto/md5
|
||||
crypto/rand
|
||||
crypto/rc4
|
||||
|
@ -76,11 +91,15 @@ go/printer
|
|||
go/scanner
|
||||
go/token
|
||||
go/types
|
||||
golang_org/x/crypto/internal/chacha20
|
||||
golang_org/x/crypto/chacha20poly1305
|
||||
golang_org/x/crypto/chacha20poly1305/internal/chacha20
|
||||
golang_org/x/crypto/cryptobyte
|
||||
golang_org/x/crypto/curve25519
|
||||
golang_org/x/crypto/poly1305
|
||||
golang_org/x/net/dns/dnsmessage
|
||||
golang_org/x/net/http/httpguts
|
||||
golang_org/x/net/http/httpproxy
|
||||
golang_org/x/net/http2/hpack
|
||||
golang_org/x/net/idna
|
||||
golang_org/x/net/lex/httplex
|
||||
|
@ -98,6 +117,7 @@ image/draw
|
|||
image/jpeg
|
||||
image/png
|
||||
index/suffixarray
|
||||
internal/cpu
|
||||
internal/poll
|
||||
internal/singleflight
|
||||
internal/trace
|
||||
|
|
|
@ -56,7 +56,7 @@ func (he headerError) Error() string {
|
|||
const (
|
||||
// Type '0' indicates a regular file.
|
||||
TypeReg = '0'
|
||||
TypeRegA = '\x00' // For legacy support; use TypeReg instead
|
||||
TypeRegA = '\x00' // Deprecated: Use TypeReg instead.
|
||||
|
||||
// Type '1' to '6' are header-only flags and may not have a data body.
|
||||
TypeLink = '1' // Hard link
|
||||
|
@ -138,7 +138,10 @@ var basicKeys = map[string]bool{
|
|||
// should do so by creating a new Header and copying the fields
|
||||
// that they are interested in preserving.
|
||||
type Header struct {
|
||||
Typeflag byte // Type of header entry (should be TypeReg for most files)
|
||||
// Typeflag is the type of header entry.
|
||||
// The zero value is automatically promoted to either TypeReg or TypeDir
|
||||
// depending on the presence of a trailing slash in Name.
|
||||
Typeflag byte
|
||||
|
||||
Name string // Name of file entry
|
||||
Linkname string // Target name of link (valid for TypeLink or TypeSymlink)
|
||||
|
@ -184,7 +187,7 @@ type Header struct {
|
|||
// The key and value should be non-empty UTF-8 strings.
|
||||
//
|
||||
// When Writer.WriteHeader is called, PAX records derived from the
|
||||
// the other fields in Header take precedence over PAXRecords.
|
||||
// other fields in Header take precedence over PAXRecords.
|
||||
PAXRecords map[string]string
|
||||
|
||||
// Format specifies the format of the tar header.
|
||||
|
|
|
@ -94,7 +94,7 @@ const (
|
|||
// application can only parse GNU formatted archives.
|
||||
//
|
||||
// Reference:
|
||||
// http://www.gnu.org/software/tar/manual/html_node/Standard.html
|
||||
// https://www.gnu.org/software/tar/manual/html_node/Standard.html
|
||||
FormatGNU
|
||||
|
||||
// Schily's tar format, which is incompatible with USTAR.
|
||||
|
|
|
@ -64,7 +64,6 @@ func (tr *Reader) next() (*Header, error) {
|
|||
// normally be visible to the outside. As such, this loop iterates through
|
||||
// one or more "header files" until it finds a "normal file".
|
||||
format := FormatUSTAR | FormatPAX | FormatGNU
|
||||
loop:
|
||||
for {
|
||||
// Discard the remainder of the file and any padding.
|
||||
if err := discard(tr.r, tr.curr.PhysicalRemaining()); err != nil {
|
||||
|
@ -102,7 +101,7 @@ loop:
|
|||
Format: format,
|
||||
}, nil
|
||||
}
|
||||
continue loop // This is a meta header affecting the next header
|
||||
continue // This is a meta header affecting the next header
|
||||
case TypeGNULongName, TypeGNULongLink:
|
||||
format.mayOnlyBe(FormatGNU)
|
||||
realname, err := ioutil.ReadAll(tr)
|
||||
|
@ -117,7 +116,7 @@ loop:
|
|||
case TypeGNULongLink:
|
||||
gnuLongLink = p.parseString(realname)
|
||||
}
|
||||
continue loop // This is a meta header affecting the next header
|
||||
continue // This is a meta header affecting the next header
|
||||
default:
|
||||
// The old GNU sparse format is handled here since it is technically
|
||||
// just a regular file with additional attributes.
|
||||
|
@ -131,8 +130,12 @@ loop:
|
|||
if gnuLongLink != "" {
|
||||
hdr.Linkname = gnuLongLink
|
||||
}
|
||||
if hdr.Typeflag == TypeRegA && strings.HasSuffix(hdr.Name, "/") {
|
||||
hdr.Typeflag = TypeDir // Legacy archives use trailing slash for directories
|
||||
if hdr.Typeflag == TypeRegA {
|
||||
if strings.HasSuffix(hdr.Name, "/") {
|
||||
hdr.Typeflag = TypeDir // Legacy archives use trailing slash for directories
|
||||
} else {
|
||||
hdr.Typeflag = TypeReg
|
||||
}
|
||||
}
|
||||
|
||||
// The extended headers may have updated the size.
|
||||
|
@ -200,7 +203,7 @@ func (tr *Reader) handleSparseFile(hdr *Header, rawHdr *block) error {
|
|||
// readGNUSparsePAXHeaders checks the PAX headers for GNU sparse headers.
|
||||
// If they are found, then this function reads the sparse map and returns it.
|
||||
// This assumes that 0.0 headers have already been converted to 0.1 headers
|
||||
// by the the PAX header parsing logic.
|
||||
// by the PAX header parsing logic.
|
||||
func (tr *Reader) readGNUSparsePAXHeaders(hdr *Header) (sparseDatas, error) {
|
||||
// Identify the version of GNU headers.
|
||||
var is1x0 bool
|
||||
|
|
|
@ -189,7 +189,7 @@ func TestReader(t *testing.T) {
|
|||
Gid: 5000,
|
||||
Size: 5,
|
||||
ModTime: time.Unix(1244593104, 0),
|
||||
Typeflag: '\x00',
|
||||
Typeflag: '0',
|
||||
}, {
|
||||
Name: "small2.txt",
|
||||
Mode: 0444,
|
||||
|
@ -197,7 +197,7 @@ func TestReader(t *testing.T) {
|
|||
Gid: 5000,
|
||||
Size: 11,
|
||||
ModTime: time.Unix(1244593104, 0),
|
||||
Typeflag: '\x00',
|
||||
Typeflag: '0',
|
||||
}},
|
||||
}, {
|
||||
file: "testdata/pax.tar",
|
||||
|
@ -378,9 +378,9 @@ func TestReader(t *testing.T) {
|
|||
"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
|
||||
},
|
||||
PAXRecords: map[string]string{
|
||||
"mtime": "1386065770.449252304",
|
||||
"atime": "1389782991.41987522",
|
||||
"ctime": "1386065770.449252304",
|
||||
"mtime": "1386065770.449252304",
|
||||
"atime": "1389782991.41987522",
|
||||
"ctime": "1386065770.449252304",
|
||||
"SCHILY.xattr.security.selinux": "unconfined_u:object_r:default_t:s0\x00",
|
||||
},
|
||||
Format: FormatPAX,
|
||||
|
@ -534,9 +534,10 @@ func TestReader(t *testing.T) {
|
|||
// a buggy pre-Go1.8 tar.Writer.
|
||||
file: "testdata/invalid-go17.tar",
|
||||
headers: []*Header{{
|
||||
Name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/foo",
|
||||
Uid: 010000000,
|
||||
ModTime: time.Unix(0, 0),
|
||||
Name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/foo",
|
||||
Uid: 010000000,
|
||||
ModTime: time.Unix(0, 0),
|
||||
Typeflag: '0',
|
||||
}},
|
||||
}, {
|
||||
// USTAR archive with a regular entry with non-zero device numbers.
|
||||
|
|
|
@ -306,6 +306,7 @@ func TestRoundTrip(t *testing.T) {
|
|||
ModTime: time.Now().Round(time.Second),
|
||||
PAXRecords: map[string]string{"uid": "2097152"},
|
||||
Format: FormatPAX,
|
||||
Typeflag: TypeReg,
|
||||
}
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
t.Fatalf("tw.WriteHeader: %v", err)
|
||||
|
|
BIN
libgo/go/archive/tar/testdata/file-and-dir.tar
vendored
Normal file
BIN
libgo/go/archive/tar/testdata/file-and-dir.tar
vendored
Normal file
Binary file not shown.
BIN
libgo/go/archive/tar/testdata/trailing-slash.tar
vendored
BIN
libgo/go/archive/tar/testdata/trailing-slash.tar
vendored
Binary file not shown.
|
@ -5,7 +5,6 @@
|
|||
package tar
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
|
@ -71,6 +70,16 @@ func (tw *Writer) WriteHeader(hdr *Header) error {
|
|||
}
|
||||
tw.hdr = *hdr // Shallow copy of Header
|
||||
|
||||
// Avoid usage of the legacy TypeRegA flag, and automatically promote
|
||||
// it to use TypeReg or TypeDir.
|
||||
if tw.hdr.Typeflag == TypeRegA {
|
||||
if strings.HasSuffix(tw.hdr.Name, "/") {
|
||||
tw.hdr.Typeflag = TypeDir
|
||||
} else {
|
||||
tw.hdr.Typeflag = TypeReg
|
||||
}
|
||||
}
|
||||
|
||||
// Round ModTime and ignore AccessTime and ChangeTime unless
|
||||
// the format is explicitly chosen.
|
||||
// This ensures nominal usage of WriteHeader (without specifying the format)
|
||||
|
@ -166,7 +175,7 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error {
|
|||
sort.Strings(keys)
|
||||
|
||||
// Write each record to a buffer.
|
||||
var buf bytes.Buffer
|
||||
var buf strings.Builder
|
||||
for _, k := range keys {
|
||||
rec, err := formatPAXRecord(k, paxHdrs[k])
|
||||
if err != nil {
|
||||
|
|
|
@ -461,6 +461,15 @@ func TestWriter(t *testing.T) {
|
|||
testHeader{Header{Name: strings.Repeat("123456789/", 30)}, nil},
|
||||
testClose{nil},
|
||||
},
|
||||
}, {
|
||||
// Automatically promote zero value of Typeflag depending on the name.
|
||||
file: "testdata/file-and-dir.tar",
|
||||
tests: []testFnc{
|
||||
testHeader{Header{Name: "small.txt", Size: 5}, nil},
|
||||
testWrite{"Kilts", 5, nil},
|
||||
testHeader{Header{Name: "dir/"}, nil},
|
||||
testClose{nil},
|
||||
},
|
||||
}}
|
||||
|
||||
equalError := func(x, y error) bool {
|
||||
|
@ -809,8 +818,8 @@ func TestValidTypeflagWithPAXHeader(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Failed to read header: %s", err)
|
||||
}
|
||||
if header.Typeflag != 0 {
|
||||
t.Fatalf("Typeflag should've been 0, found %d", header.Typeflag)
|
||||
if header.Typeflag != TypeReg {
|
||||
t.Fatalf("Typeflag should've been %d, found %d", TypeReg, header.Typeflag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,8 +81,17 @@ const (
|
|||
// See the zip spec for details.
|
||||
type FileHeader struct {
|
||||
// Name is the name of the file.
|
||||
// It must be a relative path, not start with a drive letter (e.g. C:),
|
||||
// and must use forward slashes instead of back slashes.
|
||||
//
|
||||
// It must be a relative path, not start with a drive letter (such as "C:"),
|
||||
// and must use forward slashes instead of back slashes. A trailing slash
|
||||
// indicates that this file is a directory and should have no data.
|
||||
//
|
||||
// When reading zip files, the Name field is populated from
|
||||
// the zip file directly and is not validated for correctness.
|
||||
// It is the caller's responsibility to sanitize it as
|
||||
// appropriate, including canonicalizing slash directions,
|
||||
// validating that paths are relative, and preventing path
|
||||
// traversal through filenames ("../../../").
|
||||
Name string
|
||||
|
||||
// Comment is any arbitrary user-defined string shorter than 64KiB.
|
||||
|
@ -201,7 +210,7 @@ func timeZone(offset time.Duration) *time.Location {
|
|||
|
||||
// msDosTimeToTime converts an MS-DOS date and time into a time.Time.
|
||||
// The resolution is 2s.
|
||||
// See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx
|
||||
// See: https://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx
|
||||
func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
|
||||
return time.Date(
|
||||
// date bits 0-4: day of month; 5-8: month; 9-15: years since 1980
|
||||
|
@ -221,7 +230,7 @@ func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
|
|||
|
||||
// timeToMsDosTime converts a time.Time to an MS-DOS date and time.
|
||||
// The resolution is 2s.
|
||||
// See: http://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx
|
||||
// See: https://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx
|
||||
func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) {
|
||||
fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9)
|
||||
fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11)
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"hash"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
|
@ -71,7 +72,7 @@ func (w *Writer) SetComment(comment string) error {
|
|||
}
|
||||
|
||||
// Close finishes writing the zip file by writing the central directory.
|
||||
// It does not (and cannot) close the underlying writer.
|
||||
// It does not close the underlying writer.
|
||||
func (w *Writer) Close() error {
|
||||
if w.last != nil && !w.last.closed {
|
||||
if err := w.last.close(); err != nil {
|
||||
|
@ -209,7 +210,8 @@ func (w *Writer) Close() error {
|
|||
// The file contents will be compressed using the Deflate method.
|
||||
// The name must be a relative path: it must not start with a drive
|
||||
// letter (e.g. C:) or leading slash, and only forward slashes are
|
||||
// allowed.
|
||||
// allowed. To create a directory instead of a file, add a trailing
|
||||
// slash to the name.
|
||||
// The file's contents must be written to the io.Writer before the next
|
||||
// call to Create, CreateHeader, or Close.
|
||||
func (w *Writer) Create(name string) (io.Writer, error) {
|
||||
|
@ -261,8 +263,6 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
|
|||
return nil, errors.New("archive/zip: invalid duplicate FileHeader")
|
||||
}
|
||||
|
||||
fh.Flags |= 0x8 // we will write a data descriptor
|
||||
|
||||
// The ZIP format has a sad state of affairs regarding character encoding.
|
||||
// Officially, the name and comment fields are supposed to be encoded
|
||||
// in CP-437 (which is mostly compatible with ASCII), unless the UTF-8
|
||||
|
@ -319,35 +319,58 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
|
|||
fh.Extra = append(fh.Extra, mbuf[:]...)
|
||||
}
|
||||
|
||||
fw := &fileWriter{
|
||||
zipw: w.cw,
|
||||
compCount: &countWriter{w: w.cw},
|
||||
crc32: crc32.NewIEEE(),
|
||||
}
|
||||
comp := w.compressor(fh.Method)
|
||||
if comp == nil {
|
||||
return nil, ErrAlgorithm
|
||||
}
|
||||
var err error
|
||||
fw.comp, err = comp(fw.compCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fw.rawCount = &countWriter{w: fw.comp}
|
||||
|
||||
var (
|
||||
ow io.Writer
|
||||
fw *fileWriter
|
||||
)
|
||||
h := &header{
|
||||
FileHeader: fh,
|
||||
offset: uint64(w.cw.count),
|
||||
}
|
||||
w.dir = append(w.dir, h)
|
||||
fw.header = h
|
||||
|
||||
if strings.HasSuffix(fh.Name, "/") {
|
||||
// Set the compression method to Store to ensure data length is truly zero,
|
||||
// which the writeHeader method always encodes for the size fields.
|
||||
// This is necessary as most compression formats have non-zero lengths
|
||||
// even when compressing an empty string.
|
||||
fh.Method = Store
|
||||
fh.Flags &^= 0x8 // we will not write a data descriptor
|
||||
|
||||
// Explicitly clear sizes as they have no meaning for directories.
|
||||
fh.CompressedSize = 0
|
||||
fh.CompressedSize64 = 0
|
||||
fh.UncompressedSize = 0
|
||||
fh.UncompressedSize64 = 0
|
||||
|
||||
ow = dirWriter{}
|
||||
} else {
|
||||
fh.Flags |= 0x8 // we will write a data descriptor
|
||||
|
||||
fw = &fileWriter{
|
||||
zipw: w.cw,
|
||||
compCount: &countWriter{w: w.cw},
|
||||
crc32: crc32.NewIEEE(),
|
||||
}
|
||||
comp := w.compressor(fh.Method)
|
||||
if comp == nil {
|
||||
return nil, ErrAlgorithm
|
||||
}
|
||||
var err error
|
||||
fw.comp, err = comp(fw.compCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fw.rawCount = &countWriter{w: fw.comp}
|
||||
fw.header = h
|
||||
ow = fw
|
||||
}
|
||||
w.dir = append(w.dir, h)
|
||||
if err := writeHeader(w.cw, fh); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If we're creating a directory, fw is nil.
|
||||
w.last = fw
|
||||
return fw, nil
|
||||
return ow, nil
|
||||
}
|
||||
|
||||
func writeHeader(w io.Writer, h *FileHeader) error {
|
||||
|
@ -400,6 +423,15 @@ func (w *Writer) compressor(method uint16) Compressor {
|
|||
return comp
|
||||
}
|
||||
|
||||
type dirWriter struct{}
|
||||
|
||||
func (dirWriter) Write(b []byte) (int, error) {
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
return 0, errors.New("zip: write to directory")
|
||||
}
|
||||
|
||||
type fileWriter struct {
|
||||
*header
|
||||
zipw io.Writer
|
||||
|
|
|
@ -6,6 +6,7 @@ package zip
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -299,6 +300,59 @@ func TestWriterFlush(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestWriterDir(t *testing.T) {
|
||||
w := NewWriter(ioutil.Discard)
|
||||
dw, err := w.Create("dir/")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := dw.Write(nil); err != nil {
|
||||
t.Errorf("Write(nil) to directory: got %v, want nil", err)
|
||||
}
|
||||
if _, err := dw.Write([]byte("hello")); err == nil {
|
||||
t.Error(`Write("hello") to directory: got nil error, want non-nil`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriterDirAttributes(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
w := NewWriter(&buf)
|
||||
if _, err := w.CreateHeader(&FileHeader{
|
||||
Name: "dir/",
|
||||
Method: Deflate,
|
||||
CompressedSize64: 1234,
|
||||
UncompressedSize64: 5678,
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b := buf.Bytes()
|
||||
|
||||
var sig [4]byte
|
||||
binary.LittleEndian.PutUint32(sig[:], uint32(fileHeaderSignature))
|
||||
|
||||
idx := bytes.Index(b, sig[:])
|
||||
if idx == -1 {
|
||||
t.Fatal("file header not found")
|
||||
}
|
||||
b = b[idx:]
|
||||
|
||||
if !bytes.Equal(b[6:10], []byte{0, 0, 0, 0}) { // FileHeader.Flags: 0, FileHeader.Method: 0
|
||||
t.Errorf("unexpected method and flags: %v", b[6:10])
|
||||
}
|
||||
|
||||
if !bytes.Equal(b[14:26], make([]byte, 12)) { // FileHeader.{CRC32,CompressSize,UncompressedSize} all zero.
|
||||
t.Errorf("unexpected crc, compress and uncompressed size to be 0 was: %v", b[14:26])
|
||||
}
|
||||
|
||||
binary.LittleEndian.PutUint32(sig[:], uint32(dataDescriptorSignature))
|
||||
if bytes.Index(b, sig[:]) != -1 {
|
||||
t.Error("there should be no data descriptor")
|
||||
}
|
||||
}
|
||||
|
||||
func testCreate(t *testing.T, w *Writer, wt *WriteTest) {
|
||||
header := &FileHeader{
|
||||
Name: wt.Name,
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"internal/testenv"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -140,14 +141,7 @@ func (r *rleBuffer) Write(p []byte) (n int, err error) {
|
|||
rp = &r.buf[len(r.buf)-1]
|
||||
// Fast path, if p is entirely the same byte repeated.
|
||||
if lastByte := rp.b; len(p) > 0 && p[0] == lastByte {
|
||||
all := true
|
||||
for _, b := range p {
|
||||
if b != lastByte {
|
||||
all = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if all {
|
||||
if bytes.Count(p, []byte{lastByte}) == len(p) {
|
||||
rp.n += int64(len(p))
|
||||
return len(p), nil
|
||||
}
|
||||
|
@ -165,6 +159,25 @@ func (r *rleBuffer) Write(p []byte) (n int, err error) {
|
|||
return len(p), nil
|
||||
}
|
||||
|
||||
func min(x, y int) int {
|
||||
if x < y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
func memset(a []byte, b byte) {
|
||||
if len(a) == 0 {
|
||||
return
|
||||
}
|
||||
// Double, until we reach power of 2 >= len(a), same as bytes.Repeat,
|
||||
// but without allocation.
|
||||
a[0] = b
|
||||
for i, l := 1, len(a); i < l; i *= 2 {
|
||||
copy(a[i:], a[:i])
|
||||
}
|
||||
}
|
||||
|
||||
func (r *rleBuffer) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
if len(p) == 0 {
|
||||
return
|
||||
|
@ -176,16 +189,13 @@ func (r *rleBuffer) ReadAt(p []byte, off int64) (n int, err error) {
|
|||
parts := r.buf[skipParts:]
|
||||
if len(parts) > 0 {
|
||||
skipBytes := off - parts[0].off
|
||||
for len(parts) > 0 {
|
||||
part := parts[0]
|
||||
for i := skipBytes; i < part.n; i++ {
|
||||
if n == len(p) {
|
||||
return
|
||||
}
|
||||
p[n] = part.b
|
||||
n++
|
||||
for _, part := range parts {
|
||||
repeat := min(int(part.n-skipBytes), len(p)-n)
|
||||
memset(p[n:n+repeat], part.b)
|
||||
n += repeat
|
||||
if n == len(p) {
|
||||
return
|
||||
}
|
||||
parts = parts[1:]
|
||||
skipBytes = 0
|
||||
}
|
||||
}
|
||||
|
@ -452,6 +462,9 @@ func suffixIsZip64(t *testing.T, zip sizedReaderAt) bool {
|
|||
|
||||
// Zip64 is required if the total size of the records is uint32max.
|
||||
func TestZip64LargeDirectory(t *testing.T) {
|
||||
if runtime.GOARCH == "wasm" {
|
||||
t.Skip("too slow on wasm")
|
||||
}
|
||||
if testing.Short() {
|
||||
t.Skip("skipping in short mode")
|
||||
}
|
||||
|
|
|
@ -462,6 +462,8 @@ func (b *Reader) ReadString(delim byte) (string, error) {
|
|||
|
||||
// WriteTo implements io.WriterTo.
|
||||
// This may make multiple calls to the Read method of the underlying Reader.
|
||||
// If the underlying reader supports the WriteTo method,
|
||||
// this calls the underlying WriteTo without buffering.
|
||||
func (b *Reader) WriteTo(w io.Writer) (n int64, err error) {
|
||||
n, err = b.writeBuf(w)
|
||||
if err != nil {
|
||||
|
@ -684,7 +686,9 @@ func (b *Writer) WriteString(s string) (int, error) {
|
|||
return nn, nil
|
||||
}
|
||||
|
||||
// ReadFrom implements io.ReaderFrom.
|
||||
// ReadFrom implements io.ReaderFrom. If the underlying writer
|
||||
// supports the ReadFrom method, and b has no buffered data yet,
|
||||
// this calls the underlying ReadFrom without buffering.
|
||||
func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
if b.Buffered() == 0 {
|
||||
if w, ok := b.wr.(io.ReaderFrom); ok {
|
||||
|
|
|
@ -45,14 +45,19 @@ type Scanner struct {
|
|||
// input. The arguments are an initial substring of the remaining unprocessed
|
||||
// data and a flag, atEOF, that reports whether the Reader has no more data
|
||||
// to give. The return values are the number of bytes to advance the input
|
||||
// and the next token to return to the user, plus an error, if any. If the
|
||||
// data does not yet hold a complete token, for instance if it has no newline
|
||||
// while scanning lines, SplitFunc can return (0, nil, nil) to signal the
|
||||
// Scanner to read more data into the slice and try again with a longer slice
|
||||
// starting at the same point in the input.
|
||||
// and the next token to return to the user, if any, plus an error, if any.
|
||||
//
|
||||
// If the returned error is non-nil, scanning stops and the error
|
||||
// is returned to the client.
|
||||
// Scanning stops if the function returns an error, in which case some of
|
||||
// the input may be discarded.
|
||||
//
|
||||
// Otherwise, the Scanner advances the input. If the token is not nil,
|
||||
// the Scanner returns it to the user. If the token is nil, the
|
||||
// Scanner reads more data and continues scanning; if there is no more
|
||||
// data--if atEOF was true--the Scanner returns. If the data does not
|
||||
// yet hold a complete token, for instance if it has no newline while
|
||||
// scanning lines, a SplitFunc can return (0, nil, nil) to signal the
|
||||
// Scanner to read more data into the slice and try again with a
|
||||
// longer slice starting at the same point in the input.
|
||||
//
|
||||
// The function is never called with an empty data slice unless atEOF
|
||||
// is true. If atEOF is true, however, data may be non-empty and,
|
||||
|
|
|
@ -202,6 +202,7 @@ func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) {
|
|||
b.lastRead = opInvalid
|
||||
for {
|
||||
i := b.grow(MinRead)
|
||||
b.buf = b.buf[:i]
|
||||
m, e := r.Read(b.buf[i:cap(b.buf)])
|
||||
if m < 0 {
|
||||
panic(errNegativeRead)
|
||||
|
|
|
@ -269,6 +269,39 @@ func TestReadFrom(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
type panicReader struct{ panic bool }
|
||||
|
||||
func (r panicReader) Read(p []byte) (int, error) {
|
||||
if r.panic {
|
||||
panic(nil)
|
||||
}
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
// Make sure that an empty Buffer remains empty when
|
||||
// it is "grown" before a Read that panics
|
||||
func TestReadFromPanicReader(t *testing.T) {
|
||||
|
||||
// First verify non-panic behaviour
|
||||
var buf Buffer
|
||||
i, err := buf.ReadFrom(panicReader{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if i != 0 {
|
||||
t.Fatalf("unexpected return from bytes.ReadFrom (1): got: %d, want %d", i, 0)
|
||||
}
|
||||
check(t, "TestReadFromPanicReader (1)", &buf, "")
|
||||
|
||||
// Confirm that when Reader panics, the emtpy buffer remains empty
|
||||
var buf2 Buffer
|
||||
defer func() {
|
||||
recover()
|
||||
check(t, "TestReadFromPanicReader (2)", &buf2, "")
|
||||
}()
|
||||
buf2.ReadFrom(panicReader{panic: true})
|
||||
}
|
||||
|
||||
func TestReadFromNegativeReader(t *testing.T) {
|
||||
var b Buffer
|
||||
defer func() {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package bytes
|
||||
|
||||
import (
|
||||
"internal/bytealg"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
@ -46,12 +47,16 @@ func explode(s []byte, n int) [][]byte {
|
|||
return a[0:na]
|
||||
}
|
||||
|
||||
// countGeneric actually implements Count
|
||||
func countGeneric(s, sep []byte) int {
|
||||
// Count counts the number of non-overlapping instances of sep in s.
|
||||
// If sep is an empty slice, Count returns 1 + the number of UTF-8-encoded code points in s.
|
||||
func Count(s, sep []byte) int {
|
||||
// special case
|
||||
if len(sep) == 0 {
|
||||
return utf8.RuneCount(s) + 1
|
||||
}
|
||||
if len(sep) == 1 {
|
||||
return bytealg.Count(s, sep[0])
|
||||
}
|
||||
n := 0
|
||||
for {
|
||||
i := Index(s, sep)
|
||||
|
@ -800,9 +805,9 @@ func EqualFold(s, t []byte) bool {
|
|||
tr, sr = sr, tr
|
||||
}
|
||||
// Fast check for ASCII.
|
||||
if tr < utf8.RuneSelf && 'A' <= sr && sr <= 'Z' {
|
||||
// ASCII, and sr is upper case. tr must be lower case.
|
||||
if tr == sr+'a'-'A' {
|
||||
if tr < utf8.RuneSelf {
|
||||
// ASCII only, sr/tr must be upper/lower case
|
||||
if 'A' <= sr && sr <= 'Z' && tr == sr+'a'-'A' {
|
||||
continue
|
||||
}
|
||||
return false
|
||||
|
@ -824,6 +829,92 @@ func EqualFold(s, t []byte) bool {
|
|||
return len(s) == len(t)
|
||||
}
|
||||
|
||||
// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
|
||||
func Index(s, sep []byte) int {
|
||||
n := len(sep)
|
||||
switch {
|
||||
case n == 0:
|
||||
return 0
|
||||
case n == 1:
|
||||
return IndexByte(s, sep[0])
|
||||
case n == len(s):
|
||||
if Equal(sep, s) {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
case n > len(s):
|
||||
return -1
|
||||
case n <= bytealg.MaxLen:
|
||||
// Use brute force when s and sep both are small
|
||||
if len(s) <= bytealg.MaxBruteForce {
|
||||
return bytealg.Index(s, sep)
|
||||
}
|
||||
c := sep[0]
|
||||
i := 0
|
||||
t := s[:len(s)-n+1]
|
||||
fails := 0
|
||||
for i < len(t) {
|
||||
if t[i] != c {
|
||||
// IndexByte is faster than bytealg.Index, so use it as long as
|
||||
// we're not getting lots of false positives.
|
||||
o := IndexByte(t[i:], c)
|
||||
if o < 0 {
|
||||
return -1
|
||||
}
|
||||
i += o
|
||||
}
|
||||
if Equal(s[i:i+n], sep) {
|
||||
return i
|
||||
}
|
||||
fails++
|
||||
i++
|
||||
// Switch to bytealg.Index when IndexByte produces too many false positives.
|
||||
if fails > bytealg.Cutover(i) {
|
||||
r := bytealg.Index(s[i:], sep)
|
||||
if r >= 0 {
|
||||
return r + i
|
||||
}
|
||||
return -1
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
c := sep[0]
|
||||
i := 0
|
||||
fails := 0
|
||||
t := s[:len(s)-n+1]
|
||||
for i < len(t) {
|
||||
if t[i] != c {
|
||||
o := IndexByte(t[i:], c)
|
||||
if o < 0 {
|
||||
break
|
||||
}
|
||||
i += o
|
||||
}
|
||||
if Equal(s[i:i+n], sep) {
|
||||
return i
|
||||
}
|
||||
i++
|
||||
fails++
|
||||
if fails >= 4+i>>4 && i < len(t) {
|
||||
// Give up on IndexByte, it isn't skipping ahead
|
||||
// far enough to be better than Rabin-Karp.
|
||||
// Experiments (using IndexPeriodic) suggest
|
||||
// the cutover is about 16 byte skips.
|
||||
// TODO: if large prefixes of sep are matching
|
||||
// we should cutover at even larger average skips,
|
||||
// because Equal becomes that much more expensive.
|
||||
// This code does not take that effect into account.
|
||||
j := indexRabinKarp(s[i:], sep)
|
||||
if j < 0 {
|
||||
return -1
|
||||
}
|
||||
return i + j
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func indexRabinKarp(s, sep []byte) int {
|
||||
// Rabin-Karp search
|
||||
hashsep, pow := hashStr(sep)
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package bytes
|
||||
|
||||
import "internal/cpu"
|
||||
|
||||
//go:noescape
|
||||
|
||||
// indexShortStr returns the index of the first instance of c in s, or -1 if c is not present in s.
|
||||
// indexShortStr requires 2 <= len(c) <= shortStringLen
|
||||
func indexShortStr(s, c []byte) int // ../runtime/asm_amd64.s
|
||||
func countByte(s []byte, c byte) int // ../runtime/asm_amd64.s
|
||||
|
||||
var shortStringLen int
|
||||
|
||||
func init() {
|
||||
if cpu.X86.HasAVX2 {
|
||||
shortStringLen = 63
|
||||
} else {
|
||||
shortStringLen = 31
|
||||
}
|
||||
}
|
||||
|
||||
// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
|
||||
func Index(s, sep []byte) int {
|
||||
n := len(sep)
|
||||
switch {
|
||||
case n == 0:
|
||||
return 0
|
||||
case n == 1:
|
||||
return IndexByte(s, sep[0])
|
||||
case n == len(s):
|
||||
if Equal(sep, s) {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
case n > len(s):
|
||||
return -1
|
||||
case n <= shortStringLen:
|
||||
// Use brute force when s and sep both are small
|
||||
if len(s) <= 64 {
|
||||
return indexShortStr(s, sep)
|
||||
}
|
||||
c := sep[0]
|
||||
i := 0
|
||||
t := s[:len(s)-n+1]
|
||||
fails := 0
|
||||
for i < len(t) {
|
||||
if t[i] != c {
|
||||
// IndexByte skips 16/32 bytes per iteration,
|
||||
// so it's faster than indexShortStr.
|
||||
o := IndexByte(t[i:], c)
|
||||
if o < 0 {
|
||||
return -1
|
||||
}
|
||||
i += o
|
||||
}
|
||||
if Equal(s[i:i+n], sep) {
|
||||
return i
|
||||
}
|
||||
fails++
|
||||
i++
|
||||
// Switch to indexShortStr when IndexByte produces too many false positives.
|
||||
// Too many means more that 1 error per 8 characters.
|
||||
// Allow some errors in the beginning.
|
||||
if fails > (i+16)/8 {
|
||||
r := indexShortStr(s[i:], sep)
|
||||
if r >= 0 {
|
||||
return r + i
|
||||
}
|
||||
return -1
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
return indexRabinKarp(s, sep)
|
||||
}
|
||||
|
||||
// Count counts the number of non-overlapping instances of sep in s.
|
||||
// If sep is an empty slice, Count returns 1 + the number of UTF-8-encoded code points in s.
|
||||
func Count(s, sep []byte) int {
|
||||
if len(sep) == 1 && cpu.X86.HasPOPCNT {
|
||||
return countByte(s, sep[0])
|
||||
}
|
||||
return countGeneric(s, sep)
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package bytes
|
||||
|
||||
func countByte(s []byte, c byte) int // bytes_arm64.s
|
||||
|
||||
// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
|
||||
func Index(s, sep []byte) int {
|
||||
n := len(sep)
|
||||
switch {
|
||||
case n == 0:
|
||||
return 0
|
||||
case n == 1:
|
||||
return IndexByte(s, sep[0])
|
||||
case n == len(s):
|
||||
if Equal(sep, s) {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
case n > len(s):
|
||||
return -1
|
||||
}
|
||||
c := sep[0]
|
||||
i := 0
|
||||
fails := 0
|
||||
t := s[:len(s)-n+1]
|
||||
for i < len(t) {
|
||||
if t[i] != c {
|
||||
o := IndexByte(t[i:], c)
|
||||
if o < 0 {
|
||||
break
|
||||
}
|
||||
i += o
|
||||
}
|
||||
if Equal(s[i:i+n], sep) {
|
||||
return i
|
||||
}
|
||||
i++
|
||||
fails++
|
||||
if fails >= 4+i>>4 && i < len(t) {
|
||||
// Give up on IndexByte, it isn't skipping ahead
|
||||
// far enough to be better than Rabin-Karp.
|
||||
// Experiments (using IndexPeriodic) suggest
|
||||
// the cutover is about 16 byte skips.
|
||||
// TODO: if large prefixes of sep are matching
|
||||
// we should cutover at even larger average skips,
|
||||
// because Equal becomes that much more expensive.
|
||||
// This code does not take that effect into account.
|
||||
j := indexRabinKarp(s[i:], sep)
|
||||
if j < 0 {
|
||||
return -1
|
||||
}
|
||||
return i + j
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Count counts the number of non-overlapping instances of sep in s.
|
||||
// If sep is an empty slice, Count returns 1 + the number of UTF-8-encoded code points in s.
|
||||
func Count(s, sep []byte) int {
|
||||
if len(sep) == 1 {
|
||||
return countByte(s, sep[0])
|
||||
}
|
||||
return countGeneric(s, sep)
|
||||
}
|
|
@ -6,19 +6,19 @@ package bytes
|
|||
|
||||
//go:noescape
|
||||
|
||||
// IndexByte returns the index of the first instance of c in s, or -1 if c is not present in s.
|
||||
func IndexByte(s []byte, c byte) int // ../runtime/asm_$GOARCH.s
|
||||
// IndexByte returns the index of the first instance of c in b, or -1 if c is not present in b.
|
||||
func IndexByte(b []byte, c byte) int // in internal/bytealg
|
||||
|
||||
//go:noescape
|
||||
|
||||
// Equal returns a boolean reporting whether a and b
|
||||
// are the same length and contain the same bytes.
|
||||
// A nil argument is equivalent to an empty slice.
|
||||
func Equal(a, b []byte) bool // ../runtime/asm_$GOARCH.s
|
||||
func Equal(a, b []byte) bool // in internal/bytealg
|
||||
|
||||
//go:noescape
|
||||
|
||||
// Compare returns an integer comparing two byte slices lexicographically.
|
||||
// The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
|
||||
// A nil argument is equivalent to an empty slice.
|
||||
func Compare(a, b []byte) int // ../runtime/noasm.go or ../runtime/asm_{386,amd64}.s
|
||||
func Compare(a, b []byte) int // in internal/bytealg
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// -build !amd64,!s390x,!arm64
|
||||
|
||||
package bytes
|
||||
|
||||
// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
|
||||
func Index(s, sep []byte) int {
|
||||
n := len(sep)
|
||||
switch {
|
||||
case n == 0:
|
||||
return 0
|
||||
case n == 1:
|
||||
return IndexByte(s, sep[0])
|
||||
case n == len(s):
|
||||
if Equal(sep, s) {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
case n > len(s):
|
||||
return -1
|
||||
}
|
||||
c := sep[0]
|
||||
i := 0
|
||||
fails := 0
|
||||
t := s[:len(s)-n+1]
|
||||
for i < len(t) {
|
||||
if t[i] != c {
|
||||
o := IndexByte(t[i:], c)
|
||||
if o < 0 {
|
||||
break
|
||||
}
|
||||
i += o
|
||||
}
|
||||
if Equal(s[i:i+n], sep) {
|
||||
return i
|
||||
}
|
||||
i++
|
||||
fails++
|
||||
if fails >= 4+i>>4 && i < len(t) {
|
||||
// Give up on IndexByte, it isn't skipping ahead
|
||||
// far enough to be better than Rabin-Karp.
|
||||
// Experiments (using IndexPeriodic) suggest
|
||||
// the cutover is about 16 byte skips.
|
||||
// TODO: if large prefixes of sep are matching
|
||||
// we should cutover at even larger average skips,
|
||||
// because Equal becomes that much more expensive.
|
||||
// This code does not take that effect into account.
|
||||
j := indexRabinKarp(s[i:], sep)
|
||||
if j < 0 {
|
||||
return -1
|
||||
}
|
||||
return i + j
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Count counts the number of non-overlapping instances of sep in s.
|
||||
// If sep is an empty slice, Count returns 1 + the number of UTF-8-encoded code points in s.
|
||||
func Count(s, sep []byte) int {
|
||||
return countGeneric(s, sep)
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package bytes
|
||||
|
||||
//go:noescape
|
||||
|
||||
// indexShortStr returns the index of the first instance of sep in s,
|
||||
// or -1 if sep is not present in s.
|
||||
// indexShortStr requires 2 <= len(sep) <= shortStringLen
|
||||
func indexShortStr(s, c []byte) int // ../runtime/asm_s390x.s
|
||||
|
||||
// supportsVX reports whether the vector facility is available.
|
||||
// indexShortStr must not be called if the vector facility is not
|
||||
// available.
|
||||
func supportsVX() bool // ../runtime/asm_s390x.s
|
||||
|
||||
var shortStringLen = -1
|
||||
|
||||
func init() {
|
||||
if supportsVX() {
|
||||
shortStringLen = 64
|
||||
}
|
||||
}
|
||||
|
||||
// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
|
||||
func Index(s, sep []byte) int {
|
||||
n := len(sep)
|
||||
switch {
|
||||
case n == 0:
|
||||
return 0
|
||||
case n == 1:
|
||||
return IndexByte(s, sep[0])
|
||||
case n == len(s):
|
||||
if Equal(sep, s) {
|
||||
return 0
|
||||
}
|
||||
return -1
|
||||
case n > len(s):
|
||||
return -1
|
||||
case n <= shortStringLen:
|
||||
// Use brute force when s and sep both are small
|
||||
if len(s) <= 64 {
|
||||
return indexShortStr(s, sep)
|
||||
}
|
||||
c := sep[0]
|
||||
i := 0
|
||||
t := s[:len(s)-n+1]
|
||||
fails := 0
|
||||
for i < len(t) {
|
||||
if t[i] != c {
|
||||
// IndexByte skips 16/32 bytes per iteration,
|
||||
// so it's faster than indexShortStr.
|
||||
o := IndexByte(t[i:], c)
|
||||
if o < 0 {
|
||||
return -1
|
||||
}
|
||||
i += o
|
||||
}
|
||||
if Equal(s[i:i+n], sep) {
|
||||
return i
|
||||
}
|
||||
fails++
|
||||
i++
|
||||
// Switch to indexShortStr when IndexByte produces too many false positives.
|
||||
// Too many means more that 1 error per 8 characters.
|
||||
// Allow some errors in the beginning.
|
||||
if fails > (i+16)/8 {
|
||||
r := indexShortStr(s[i:], sep)
|
||||
if r >= 0 {
|
||||
return r + i
|
||||
}
|
||||
return -1
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
return indexRabinKarp(s, sep)
|
||||
}
|
||||
|
||||
// Count counts the number of non-overlapping instances of sep in s.
|
||||
// If sep is an empty slice, Count returns 1 + the number of UTF-8-encoded code points in s.
|
||||
func Count(s, sep []byte) int {
|
||||
return countGeneric(s, sep)
|
||||
}
|
|
@ -415,10 +415,6 @@ func TestCountByte(t *testing.T) {
|
|||
if p != j+1 {
|
||||
t.Errorf("TestCountByte.Count(%q, 100) = %d", b[i:i+window], p)
|
||||
}
|
||||
pGeneric := CountGeneric(b[i:i+window], []byte{100})
|
||||
if pGeneric != j+1 {
|
||||
t.Errorf("TestCountByte.CountGeneric(%q, 100) = %d", b[i:i+window], p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -466,10 +462,6 @@ func TestCountByteNoMatch(t *testing.T) {
|
|||
if p != 0 {
|
||||
t.Errorf("TestCountByteNoMatch(%q, 0) = %d", b[i:i+window], p)
|
||||
}
|
||||
pGeneric := CountGeneric(b[i:i+window], []byte{0})
|
||||
if pGeneric != 0 {
|
||||
t.Errorf("TestCountByteNoMatch.CountGeneric(%q, 100) = %d", b[i:i+window], p)
|
||||
}
|
||||
for j := 0; j < window; j++ {
|
||||
b[i+j] = byte(0)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ package bytes_test
|
|||
|
||||
import (
|
||||
. "bytes"
|
||||
"internal/testenv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -58,10 +59,20 @@ func TestCompareIdenticalSlice(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCompareBytes(t *testing.T) {
|
||||
n := 128
|
||||
lengths := make([]int, 0) // lengths to test in ascending order
|
||||
for i := 0; i <= 128; i++ {
|
||||
lengths = append(lengths, i)
|
||||
}
|
||||
lengths = append(lengths, 256, 512, 1024, 1333, 4095, 4096, 4097)
|
||||
|
||||
if !testing.Short() || testenv.Builder() != "" {
|
||||
lengths = append(lengths, 65535, 65536, 65537, 99999)
|
||||
}
|
||||
|
||||
n := lengths[len(lengths)-1]
|
||||
a := make([]byte, n+1)
|
||||
b := make([]byte, n+1)
|
||||
for len := 0; len < 128; len++ {
|
||||
for _, len := range lengths {
|
||||
// randomish but deterministic data. No 0 or 255.
|
||||
for i := 0; i < len; i++ {
|
||||
a[i] = byte(1 + 31*i%254)
|
||||
|
|
|
@ -7,4 +7,3 @@ package bytes
|
|||
// Export func for testing
|
||||
var IndexBytePortable = indexBytePortable
|
||||
var EqualPortable = equalPortable
|
||||
var CountGeneric = countGeneric
|
||||
|
|
|
@ -22,6 +22,21 @@ func usage() {
|
|||
|
||||
var wflag = flag.Bool("w", false, "write build ID")
|
||||
|
||||
// taken from cmd/go/internal/work/buildid.go
|
||||
func hashToString(h [32]byte) string {
|
||||
const b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||
const chunks = 5
|
||||
var dst [chunks * 4]byte
|
||||
for i := 0; i < chunks; i++ {
|
||||
v := uint32(h[3*i])<<16 | uint32(h[3*i+1])<<8 | uint32(h[3*i+2])
|
||||
dst[4*i+0] = b64[(v>>18)&0x3F]
|
||||
dst[4*i+1] = b64[(v>>12)&0x3F]
|
||||
dst[4*i+2] = b64[(v>>6)&0x3F]
|
||||
dst[4*i+3] = b64[v&0x3F]
|
||||
}
|
||||
return string(dst[:])
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetPrefix("buildid: ")
|
||||
log.SetFlags(0)
|
||||
|
@ -41,6 +56,8 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
// Keep in sync with src/cmd/go/internal/work/buildid.go:updateBuildID
|
||||
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -51,14 +68,14 @@ func main() {
|
|||
}
|
||||
f.Close()
|
||||
|
||||
tail := id
|
||||
if i := strings.LastIndex(id, "."); i >= 0 {
|
||||
tail = tail[i+1:]
|
||||
newID := id[:strings.LastIndex(id, "/")] + "/" + hashToString(hash)
|
||||
if len(newID) != len(id) {
|
||||
log.Fatalf("%s: build ID length mismatch %q vs %q", file, id, newID)
|
||||
}
|
||||
if len(tail) != len(hash)*2 {
|
||||
log.Fatalf("%s: cannot find %d-byte hash in id %s", file, len(hash), id)
|
||||
|
||||
if len(matches) == 0 {
|
||||
return
|
||||
}
|
||||
newID := id[:len(id)-len(tail)] + fmt.Sprintf("%x", hash)
|
||||
|
||||
f, err = os.OpenFile(file, os.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
|
|
|
@ -95,7 +95,7 @@ func (f *File) ParseGo(name string, src []byte) {
|
|||
}
|
||||
}
|
||||
if !sawC {
|
||||
error_(token.NoPos, `cannot find import "C"`)
|
||||
error_(ast1.Package, `cannot find import "C"`)
|
||||
}
|
||||
|
||||
// In ast2, strip the import "C" line.
|
||||
|
@ -356,6 +356,7 @@ func (f *File) walk(x interface{}, context astContext, visit func(*File, interfa
|
|||
case *ast.BadExpr:
|
||||
case *ast.Ident:
|
||||
case *ast.Ellipsis:
|
||||
f.walk(&n.Elt, ctxType, visit)
|
||||
case *ast.BasicLit:
|
||||
case *ast.FuncLit:
|
||||
f.walk(n.Type, ctxType, visit)
|
||||
|
|
|
@ -64,6 +64,11 @@ a full argument: to allow -mfoo=bar, use CGO_CFLAGS_ALLOW='-mfoo.*',
|
|||
not just CGO_CFLAGS_ALLOW='-mfoo'. Similarly named variables control
|
||||
the allowed CPPFLAGS, CXXFLAGS, FFLAGS, and LDFLAGS.
|
||||
|
||||
Also for security reasons, only a limited set of characters are
|
||||
permitted, notably alphanumeric characters and a few symbols, such as
|
||||
'.', that will not be interpreted in unexpected ways. Attempts to use
|
||||
forbidden characters will get a "malformed #cgo argument" error.
|
||||
|
||||
When building, the CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS, CGO_FFLAGS and
|
||||
CGO_LDFLAGS environment variables are added to the flags derived from
|
||||
these directives. Package-specific flags should be set using the
|
||||
|
@ -99,17 +104,24 @@ compiled with the C compiler. Any .cc, .cpp, or .cxx files will be
|
|||
compiled with the C++ compiler. Any .f, .F, .for or .f90 files will be
|
||||
compiled with the fortran compiler. Any .h, .hh, .hpp, or .hxx files will
|
||||
not be compiled separately, but, if these header files are changed,
|
||||
the C and C++ files will be recompiled. The default C and C++
|
||||
compilers may be changed by the CC and CXX environment variables,
|
||||
respectively; those environment variables may include command line
|
||||
options.
|
||||
the package (including its non-Go source files) will be recompiled.
|
||||
Note that changes to files in other directories do not cause the package
|
||||
to be recompiled, so all non-Go source code for the package should be
|
||||
stored in the package directory, not in subdirectories.
|
||||
The default C and C++ compilers may be changed by the CC and CXX
|
||||
environment variables, respectively; those environment variables
|
||||
may include command line options.
|
||||
|
||||
The cgo tool is enabled by default for native builds on systems where
|
||||
it is expected to work. It is disabled by default when
|
||||
cross-compiling. You can control this by setting the CGO_ENABLED
|
||||
environment variable when running the go tool: set it to 1 to enable
|
||||
the use of cgo, and to 0 to disable it. The go tool will set the
|
||||
build constraint "cgo" if cgo is enabled.
|
||||
build constraint "cgo" if cgo is enabled. The special import "C"
|
||||
implies the "cgo" build constraint, as though the file also said
|
||||
"// +build cgo". Therefore, if cgo is disabled, files that import
|
||||
"C" will not be built by the go tool. (For more about build constraints
|
||||
see https://golang.org/pkg/go/build/#hdr-Build_Constraints).
|
||||
|
||||
When cross-compiling, you must specify a C cross-compiler for cgo to
|
||||
use. You can do this by setting the generic CC_FOR_TARGET or the
|
||||
|
@ -219,6 +231,26 @@ C compilers are aware of this calling convention and adjust
|
|||
the call accordingly, but Go cannot. In Go, you must pass
|
||||
the pointer to the first element explicitly: C.f(&C.x[0]).
|
||||
|
||||
Calling variadic C functions is not supported. It is possible to
|
||||
circumvent this by using a C function wrapper. For example:
|
||||
|
||||
package main
|
||||
|
||||
// #include <stdio.h>
|
||||
// #include <stdlib.h>
|
||||
//
|
||||
// static void myprint(char* s) {
|
||||
// printf("%s\n", s);
|
||||
// }
|
||||
import "C"
|
||||
import "unsafe"
|
||||
|
||||
func main() {
|
||||
cs := C.CString("Hello from stdio")
|
||||
C.myprint(cs)
|
||||
C.free(unsafe.Pointer(cs))
|
||||
}
|
||||
|
||||
A few special functions convert between Go and C types
|
||||
by making copies of the data. In pseudo-Go definitions:
|
||||
|
||||
|
@ -348,6 +380,14 @@ and of course there is nothing stopping the C code from doing anything
|
|||
it likes. However, programs that break these rules are likely to fail
|
||||
in unexpected and unpredictable ways.
|
||||
|
||||
Note: the current implementation has a bug. While Go code is permitted
|
||||
to write nil or a C pointer (but not a Go pointer) to C memory, the
|
||||
current implementation may sometimes cause a runtime error if the
|
||||
contents of the C memory appear to be a Go pointer. Therefore, avoid
|
||||
passing uninitialized C memory to Go code if the Go code is going to
|
||||
store pointer values in it. Zero out the memory in C before passing it
|
||||
to Go.
|
||||
|
||||
Special cases
|
||||
|
||||
A few special C types which would normally be represented by a pointer
|
||||
|
|
|
@ -183,9 +183,29 @@ func (p *Package) Translate(f *File) {
|
|||
cref.Name.C = cname(cref.Name.Go)
|
||||
}
|
||||
p.loadDefines(f)
|
||||
needType := p.guessKinds(f)
|
||||
if len(needType) > 0 {
|
||||
p.loadDWARF(f, needType)
|
||||
p.typedefs = map[string]bool{}
|
||||
p.typedefList = nil
|
||||
numTypedefs := -1
|
||||
for len(p.typedefs) > numTypedefs {
|
||||
numTypedefs = len(p.typedefs)
|
||||
// Also ask about any typedefs we've seen so far.
|
||||
for _, a := range p.typedefList {
|
||||
f.Name[a] = &Name{
|
||||
Go: a,
|
||||
C: a,
|
||||
}
|
||||
}
|
||||
needType := p.guessKinds(f)
|
||||
if len(needType) > 0 {
|
||||
p.loadDWARF(f, needType)
|
||||
}
|
||||
|
||||
// In godefs mode we're OK with the typedefs, which
|
||||
// will presumably also be defined in the file, we
|
||||
// don't want to resolve them to their base types.
|
||||
if *godefs {
|
||||
break
|
||||
}
|
||||
}
|
||||
if p.rewriteCalls(f) {
|
||||
// Add `import _cgo_unsafe "unsafe"` after the package statement.
|
||||
|
@ -570,6 +590,7 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
|
|||
fatalf("malformed __cgo__ name: %s", name)
|
||||
}
|
||||
types[i] = t.Type
|
||||
p.recordTypedefs(t.Type)
|
||||
}
|
||||
if e.Tag != dwarf.TagCompileUnit {
|
||||
r.SkipChildren()
|
||||
|
@ -605,7 +626,25 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
|
|||
}
|
||||
}
|
||||
case "fconst":
|
||||
if i < len(floats) {
|
||||
if i >= len(floats) {
|
||||
break
|
||||
}
|
||||
switch base(types[i]).(type) {
|
||||
case *dwarf.IntType, *dwarf.UintType:
|
||||
// This has an integer type so it's
|
||||
// not really a floating point
|
||||
// constant. This can happen when the
|
||||
// C compiler complains about using
|
||||
// the value as an integer constant,
|
||||
// but not as a general constant.
|
||||
// Treat this as a variable of the
|
||||
// appropriate type, not a constant,
|
||||
// to get C-style type handling,
|
||||
// avoiding the problem that C permits
|
||||
// uint64(-1) but Go does not.
|
||||
// See issue 26066.
|
||||
n.Kind = "var"
|
||||
default:
|
||||
n.Const = fmt.Sprintf("%f", floats[i])
|
||||
}
|
||||
case "sconst":
|
||||
|
@ -618,6 +657,47 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
|
|||
}
|
||||
}
|
||||
|
||||
// recordTypedefs remembers in p.typedefs all the typedefs used in dtypes and its children.
|
||||
func (p *Package) recordTypedefs(dtype dwarf.Type) {
|
||||
p.recordTypedefs1(dtype, map[dwarf.Type]bool{})
|
||||
}
|
||||
func (p *Package) recordTypedefs1(dtype dwarf.Type, visited map[dwarf.Type]bool) {
|
||||
if dtype == nil {
|
||||
return
|
||||
}
|
||||
if visited[dtype] {
|
||||
return
|
||||
}
|
||||
visited[dtype] = true
|
||||
switch dt := dtype.(type) {
|
||||
case *dwarf.TypedefType:
|
||||
if strings.HasPrefix(dt.Name, "__builtin") {
|
||||
// Don't look inside builtin types. There be dragons.
|
||||
return
|
||||
}
|
||||
if !p.typedefs[dt.Name] {
|
||||
p.typedefs[dt.Name] = true
|
||||
p.typedefList = append(p.typedefList, dt.Name)
|
||||
p.recordTypedefs1(dt.Type, visited)
|
||||
}
|
||||
case *dwarf.PtrType:
|
||||
p.recordTypedefs1(dt.Type, visited)
|
||||
case *dwarf.ArrayType:
|
||||
p.recordTypedefs1(dt.Type, visited)
|
||||
case *dwarf.QualType:
|
||||
p.recordTypedefs1(dt.Type, visited)
|
||||
case *dwarf.FuncType:
|
||||
p.recordTypedefs1(dt.ReturnType, visited)
|
||||
for _, a := range dt.ParamType {
|
||||
p.recordTypedefs1(a, visited)
|
||||
}
|
||||
case *dwarf.StructType:
|
||||
for _, f := range dt.Field {
|
||||
p.recordTypedefs1(f.Type, visited)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mangleName does name mangling to translate names
|
||||
// from the original Go source files to the names
|
||||
// used in the final Go files generated by cgo.
|
||||
|
@ -1373,7 +1453,7 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6
|
|||
if len(data) <= strlen {
|
||||
fatalf("invalid string literal")
|
||||
}
|
||||
strs[n] = string(data[:strlen])
|
||||
strs[n] = data[:strlen]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1754,6 +1834,7 @@ type typeConv struct {
|
|||
// Map from types to incomplete pointers to those types.
|
||||
ptrs map[dwarf.Type][]*Type
|
||||
// Keys of ptrs in insertion order (deterministic worklist)
|
||||
// ptrKeys contains exactly the keys in ptrs.
|
||||
ptrKeys []dwarf.Type
|
||||
|
||||
// Type names X for which there exists an XGetTypeID function with type func() CFTypeID.
|
||||
|
@ -1896,14 +1977,15 @@ func (c *typeConv) FinishType(pos token.Pos) {
|
|||
for len(c.ptrKeys) > 0 {
|
||||
dtype := c.ptrKeys[0]
|
||||
c.ptrKeys = c.ptrKeys[1:]
|
||||
ptrs := c.ptrs[dtype]
|
||||
delete(c.ptrs, dtype)
|
||||
|
||||
// Note Type might invalidate c.ptrs[dtype].
|
||||
t := c.Type(dtype, pos)
|
||||
for _, ptr := range c.ptrs[dtype] {
|
||||
for _, ptr := range ptrs {
|
||||
ptr.Go.(*ast.StarExpr).X = t.Go
|
||||
ptr.C.Set("%s*", t.C)
|
||||
}
|
||||
c.ptrs[dtype] = nil // retain the map key
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2180,6 +2262,10 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
|
|||
s := *sub
|
||||
s.Go = c.uintptr
|
||||
sub = &s
|
||||
// Make sure we update any previously computed type.
|
||||
if oldType := typedef[name.Name]; oldType != nil {
|
||||
oldType.Go = sub.Go
|
||||
}
|
||||
}
|
||||
t.Go = name
|
||||
if unionWithPointer[sub.Go] {
|
||||
|
@ -2341,7 +2427,7 @@ func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type {
|
|||
}
|
||||
// ...or the typedef is one in which we expect bad pointers.
|
||||
// It will be a uintptr instead of *X.
|
||||
if c.badPointerTypedef(dt) {
|
||||
if c.baseBadPointerTypedef(dt) {
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -2693,6 +2779,19 @@ func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// baseBadPointerTypedef reports whether the base of a chain of typedefs is a bad typedef
|
||||
// as badPointerTypedef reports.
|
||||
func (c *typeConv) baseBadPointerTypedef(dt *dwarf.TypedefType) bool {
|
||||
for {
|
||||
if t, ok := dt.Type.(*dwarf.TypedefType); ok {
|
||||
dt = t
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
return c.badPointerTypedef(dt)
|
||||
}
|
||||
|
||||
func (c *typeConv) badCFType(dt *dwarf.TypedefType) bool {
|
||||
// The real bad types are CFNumberRef and CFDateRef.
|
||||
// Sometimes non-pointers are stored in these types.
|
||||
|
@ -2781,13 +2880,31 @@ func (c *typeConv) badJNI(dt *dwarf.TypedefType) bool {
|
|||
}
|
||||
}
|
||||
|
||||
// Check that the typedef is:
|
||||
// struct _jobject;
|
||||
// typedef struct _jobject *jobject;
|
||||
// Check that the typedef is either:
|
||||
// 1:
|
||||
// struct _jobject;
|
||||
// typedef struct _jobject *jobject;
|
||||
// 2: (in NDK16 in C++)
|
||||
// class _jobject {};
|
||||
// typedef _jobject* jobject;
|
||||
// 3: (in NDK16 in C)
|
||||
// typedef void* jobject;
|
||||
if ptr, ok := w.Type.(*dwarf.PtrType); ok {
|
||||
if str, ok := ptr.Type.(*dwarf.StructType); ok {
|
||||
if str.StructName == "_jobject" && str.Kind == "struct" && len(str.Field) == 0 && str.Incomplete {
|
||||
return true
|
||||
switch v := ptr.Type.(type) {
|
||||
case *dwarf.VoidType:
|
||||
return true
|
||||
case *dwarf.StructType:
|
||||
if v.StructName == "_jobject" && len(v.Field) == 0 {
|
||||
switch v.Kind {
|
||||
case "struct":
|
||||
if v.Incomplete {
|
||||
return true
|
||||
}
|
||||
case "class":
|
||||
if !v.Incomplete {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2796,7 +2913,7 @@ func (c *typeConv) badJNI(dt *dwarf.TypedefType) bool {
|
|||
}
|
||||
|
||||
// jniTypes maps from JNI types that we want to be uintptrs, to the underlying type to which
|
||||
// they are mapped. The base "jobject" maps to the empty string.
|
||||
// they are mapped. The base "jobject" maps to the empty string.
|
||||
var jniTypes = map[string]string{
|
||||
"jobject": "",
|
||||
"jclass": "jobject",
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
func (p *Package) godefs(f *File, srcfile string) string {
|
||||
var buf bytes.Buffer
|
||||
|
||||
fmt.Fprintf(&buf, "// Created by cgo -godefs - DO NOT EDIT\n")
|
||||
fmt.Fprintf(&buf, "// Code generated by cmd/cgo -godefs; DO NOT EDIT.\n")
|
||||
fmt.Fprintf(&buf, "// %s %s\n", filepath.Base(os.Args[0]), strings.Join(os.Args[1:], " "))
|
||||
fmt.Fprintf(&buf, "\n")
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"go/ast"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -42,9 +43,11 @@ type Package struct {
|
|||
Name map[string]*Name // accumulated Name from Files
|
||||
ExpFunc []*ExpFunc // accumulated ExpFunc from Files
|
||||
Decl []ast.Decl
|
||||
GoFiles []string // list of Go files
|
||||
GccFiles []string // list of gcc output files
|
||||
Preamble string // collected preamble for _cgo_export.h
|
||||
GoFiles []string // list of Go files
|
||||
GccFiles []string // list of gcc output files
|
||||
Preamble string // collected preamble for _cgo_export.h
|
||||
typedefs map[string]bool // type names that appear in the types of the objects we're interested in
|
||||
typedefList []string
|
||||
}
|
||||
|
||||
// A File collects information about a single Go input file.
|
||||
|
@ -278,6 +281,9 @@ func main() {
|
|||
if arg == "-fsanitize=thread" {
|
||||
tsanProlog = yesTsanProlog
|
||||
}
|
||||
if arg == "-fsanitize=memory" {
|
||||
msanProlog = yesMsanProlog
|
||||
}
|
||||
}
|
||||
|
||||
p := newPackage(args[:i])
|
||||
|
@ -297,6 +303,7 @@ func main() {
|
|||
// concern is other cgo wrappers for the same functions.
|
||||
// Use the beginning of the md5 of the input to disambiguate.
|
||||
h := md5.New()
|
||||
io.WriteString(h, *importPath)
|
||||
fs := make([]*File, len(goFiles))
|
||||
for i, input := range goFiles {
|
||||
if *srcDir != "" {
|
||||
|
@ -410,6 +417,14 @@ func (p *Package) Record(f *File) {
|
|||
for k, v := range f.Name {
|
||||
if p.Name[k] == nil {
|
||||
p.Name[k] = v
|
||||
} else if p.incompleteTypedef(p.Name[k].Type) {
|
||||
p.Name[k] = v
|
||||
} else if p.incompleteTypedef(v.Type) {
|
||||
// Nothing to do.
|
||||
} else if _, ok := nameToC[k]; ok {
|
||||
// Names we predefine may appear inconsistent
|
||||
// if some files typedef them and some don't.
|
||||
// Issue 26743.
|
||||
} else if !reflect.DeepEqual(p.Name[k], v) {
|
||||
error_(token.NoPos, "inconsistent definitions for C.%s", fixGo(k))
|
||||
}
|
||||
|
@ -422,3 +437,9 @@ func (p *Package) Record(f *File) {
|
|||
}
|
||||
p.Decl = append(p.Decl, f.AST.Decls...)
|
||||
}
|
||||
|
||||
// incompleteTypedef reports whether t appears to be an incomplete
|
||||
// typedef definition.
|
||||
func (p *Package) incompleteTypedef(t *Type) bool {
|
||||
return t == nil || (t.Size == 0 && t.Align == -1)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
@ -78,7 +79,7 @@ func (p *Package) writeDefs() {
|
|||
// Write second Go output: definitions of _C_xxx.
|
||||
// In a separate file so that the import of "unsafe" does not
|
||||
// pollute the original file.
|
||||
fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n\n")
|
||||
fmt.Fprintf(fgo2, "// Code generated by cmd/cgo; DO NOT EDIT.\n\n")
|
||||
fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName)
|
||||
fmt.Fprintf(fgo2, "import \"unsafe\"\n\n")
|
||||
if !*gccgo && *importRuntimeCgo {
|
||||
|
@ -277,10 +278,7 @@ func dynimport(obj string) {
|
|||
}
|
||||
}
|
||||
}
|
||||
sym, err := f.ImportedSymbols()
|
||||
if err != nil {
|
||||
fatalf("cannot load imported symbols from ELF file %s: %v", obj, err)
|
||||
}
|
||||
sym, _ := f.ImportedSymbols()
|
||||
for _, s := range sym {
|
||||
targ := s.Name
|
||||
if s.Version != "" {
|
||||
|
@ -288,10 +286,7 @@ func dynimport(obj string) {
|
|||
}
|
||||
fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s.Name, targ, s.Library)
|
||||
}
|
||||
lib, err := f.ImportedLibraries()
|
||||
if err != nil {
|
||||
fatalf("cannot load imported libraries from ELF file %s: %v", obj, err)
|
||||
}
|
||||
lib, _ := f.ImportedLibraries()
|
||||
for _, l := range lib {
|
||||
fmt.Fprintf(stdout, "//go:cgo_import_dynamic _ _ %q\n", l)
|
||||
}
|
||||
|
@ -299,20 +294,14 @@ func dynimport(obj string) {
|
|||
}
|
||||
|
||||
if f, err := macho.Open(obj); err == nil {
|
||||
sym, err := f.ImportedSymbols()
|
||||
if err != nil {
|
||||
fatalf("cannot load imported symbols from Mach-O file %s: %v", obj, err)
|
||||
}
|
||||
sym, _ := f.ImportedSymbols()
|
||||
for _, s := range sym {
|
||||
if len(s) > 0 && s[0] == '_' {
|
||||
s = s[1:]
|
||||
}
|
||||
fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s, s, "")
|
||||
}
|
||||
lib, err := f.ImportedLibraries()
|
||||
if err != nil {
|
||||
fatalf("cannot load imported libraries from Mach-O file %s: %v", obj, err)
|
||||
}
|
||||
lib, _ := f.ImportedLibraries()
|
||||
for _, l := range lib {
|
||||
fmt.Fprintf(stdout, "//go:cgo_import_dynamic _ _ %q\n", l)
|
||||
}
|
||||
|
@ -320,10 +309,7 @@ func dynimport(obj string) {
|
|||
}
|
||||
|
||||
if f, err := pe.Open(obj); err == nil {
|
||||
sym, err := f.ImportedSymbols()
|
||||
if err != nil {
|
||||
fatalf("cannot load imported symbols from PE file %s: %v", obj, err)
|
||||
}
|
||||
sym, _ := f.ImportedSymbols()
|
||||
for _, s := range sym {
|
||||
ss := strings.Split(s, ":")
|
||||
name := strings.Split(ss[0], "@")[0]
|
||||
|
@ -559,8 +545,8 @@ func (p *Package) writeOutput(f *File, srcfile string) {
|
|||
p.GccFiles = append(p.GccFiles, base+".cgo2.c")
|
||||
|
||||
// Write Go output: Go input with rewrites of C.xxx to _C_xxx.
|
||||
fmt.Fprintf(fgo1, "// Created by cgo - DO NOT EDIT\n\n")
|
||||
fmt.Fprintf(fgo1, "//line %s:1\n", srcfile)
|
||||
fmt.Fprintf(fgo1, "// Code generated by cmd/cgo; DO NOT EDIT.\n\n")
|
||||
fmt.Fprintf(fgo1, "//line %s:1:1\n", srcfile)
|
||||
fgo1.Write(f.Edit.Bytes())
|
||||
|
||||
// While we process the vars and funcs, also write gcc output.
|
||||
|
@ -569,6 +555,7 @@ func (p *Package) writeOutput(f *File, srcfile string) {
|
|||
fmt.Fprintf(fgcc, "%s\n", f.Preamble)
|
||||
fmt.Fprintf(fgcc, "%s\n", gccProlog)
|
||||
fmt.Fprintf(fgcc, "%s\n", tsanProlog)
|
||||
fmt.Fprintf(fgcc, "%s\n", msanProlog)
|
||||
|
||||
for _, key := range nameKeys(f.Name) {
|
||||
n := f.Name[key]
|
||||
|
@ -632,14 +619,14 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
|
|||
// We're trying to write a gcc struct that matches gc's layout.
|
||||
// Use packed attribute to force no padding in this struct in case
|
||||
// gcc has different packing requirements.
|
||||
fmt.Fprintf(fgcc, "\t%s %v *a = v;\n", ctype, p.packedAttribute())
|
||||
fmt.Fprintf(fgcc, "\t%s %v *_cgo_a = v;\n", ctype, p.packedAttribute())
|
||||
if n.FuncType.Result != nil {
|
||||
// Save the stack top for use below.
|
||||
fmt.Fprintf(fgcc, "\tchar *stktop = _cgo_topofstack();\n")
|
||||
fmt.Fprintf(fgcc, "\tchar *_cgo_stktop = _cgo_topofstack();\n")
|
||||
}
|
||||
tr := n.FuncType.Result
|
||||
if tr != nil {
|
||||
fmt.Fprintf(fgcc, "\t__typeof__(a->r) r;\n")
|
||||
fmt.Fprintf(fgcc, "\t__typeof__(_cgo_a->r) _cgo_r;\n")
|
||||
}
|
||||
fmt.Fprintf(fgcc, "\t_cgo_tsan_acquire();\n")
|
||||
if n.AddError {
|
||||
|
@ -647,9 +634,9 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
|
|||
}
|
||||
fmt.Fprintf(fgcc, "\t")
|
||||
if tr != nil {
|
||||
fmt.Fprintf(fgcc, "r = ")
|
||||
fmt.Fprintf(fgcc, "_cgo_r = ")
|
||||
if c := tr.C.String(); c[len(c)-1] == '*' {
|
||||
fmt.Fprint(fgcc, "(__typeof__(a->r)) ")
|
||||
fmt.Fprint(fgcc, "(__typeof__(_cgo_a->r)) ")
|
||||
}
|
||||
}
|
||||
if n.Kind == "macro" {
|
||||
|
@ -660,7 +647,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
|
|||
if i > 0 {
|
||||
fmt.Fprintf(fgcc, ", ")
|
||||
}
|
||||
fmt.Fprintf(fgcc, "a->p%d", i)
|
||||
fmt.Fprintf(fgcc, "_cgo_a->p%d", i)
|
||||
}
|
||||
fmt.Fprintf(fgcc, ");\n")
|
||||
}
|
||||
|
@ -671,9 +658,19 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
|
|||
if n.FuncType.Result != nil {
|
||||
// The cgo call may have caused a stack copy (via a callback).
|
||||
// Adjust the return value pointer appropriately.
|
||||
fmt.Fprintf(fgcc, "\ta = (void*)((char*)a + (_cgo_topofstack() - stktop));\n")
|
||||
fmt.Fprintf(fgcc, "\t_cgo_a = (void*)((char*)_cgo_a + (_cgo_topofstack() - _cgo_stktop));\n")
|
||||
// Save the return value.
|
||||
fmt.Fprintf(fgcc, "\ta->r = r;\n")
|
||||
fmt.Fprintf(fgcc, "\t_cgo_a->r = _cgo_r;\n")
|
||||
// The return value is on the Go stack. If we are using msan,
|
||||
// and if the C value is partially or completely uninitialized,
|
||||
// the assignment will mark the Go stack as uninitialized.
|
||||
// The Go compiler does not update msan for changes to the
|
||||
// stack. It is possible that the stack will remain
|
||||
// uninitialized, and then later be used in a way that is
|
||||
// visible to msan, possibly leading to a false positive.
|
||||
// Mark the stack space as written, to avoid this problem.
|
||||
// See issue 26209.
|
||||
fmt.Fprintf(fgcc, "\t_cgo_msan_write(&_cgo_a->r, sizeof(_cgo_a->r));\n")
|
||||
}
|
||||
if n.AddError {
|
||||
fmt.Fprintf(fgcc, "\treturn _cgo_errno;\n")
|
||||
|
@ -708,12 +705,12 @@ func (p *Package) writeGccgoOutputFunc(fgcc *os.File, n *Name) {
|
|||
fmt.Fprintf(fgcc, ")\n")
|
||||
fmt.Fprintf(fgcc, "{\n")
|
||||
if t := n.FuncType.Result; t != nil {
|
||||
fmt.Fprintf(fgcc, "\t%s r;\n", t.C.String())
|
||||
fmt.Fprintf(fgcc, "\t%s _cgo_r;\n", t.C.String())
|
||||
}
|
||||
fmt.Fprintf(fgcc, "\t_cgo_tsan_acquire();\n")
|
||||
fmt.Fprintf(fgcc, "\t")
|
||||
if t := n.FuncType.Result; t != nil {
|
||||
fmt.Fprintf(fgcc, "r = ")
|
||||
fmt.Fprintf(fgcc, "_cgo_r = ")
|
||||
// Cast to void* to avoid warnings due to omitted qualifiers.
|
||||
if c := t.C.String(); c[len(c)-1] == '*' {
|
||||
fmt.Fprintf(fgcc, "(void*)")
|
||||
|
@ -739,7 +736,7 @@ func (p *Package) writeGccgoOutputFunc(fgcc *os.File, n *Name) {
|
|||
if c := t.C.String(); c[len(c)-1] == '*' {
|
||||
fmt.Fprintf(fgcc, "(void*)")
|
||||
}
|
||||
fmt.Fprintf(fgcc, "r;\n")
|
||||
fmt.Fprintf(fgcc, "_cgo_r;\n")
|
||||
}
|
||||
fmt.Fprintf(fgcc, "}\n")
|
||||
fmt.Fprintf(fgcc, "\n")
|
||||
|
@ -748,7 +745,7 @@ func (p *Package) writeGccgoOutputFunc(fgcc *os.File, n *Name) {
|
|||
// packedAttribute returns host compiler struct attribute that will be
|
||||
// used to match gc's struct layout. For example, on 386 Windows,
|
||||
// gcc wants to 8-align int64s, but gc does not.
|
||||
// Use __gcc_struct__ to work around http://gcc.gnu.org/PR52991 on x86,
|
||||
// Use __gcc_struct__ to work around https://gcc.gnu.org/PR52991 on x86,
|
||||
// and https://golang.org/issue/5603.
|
||||
func (p *Package) packedAttribute() string {
|
||||
s := "__attribute__((__packed__"
|
||||
|
@ -763,7 +760,7 @@ func (p *Package) packedAttribute() string {
|
|||
func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
|
||||
p.writeExportHeader(fgcch)
|
||||
|
||||
fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n")
|
||||
fmt.Fprintf(fgcc, "/* Code generated by cmd/cgo; DO NOT EDIT. */\n\n")
|
||||
fmt.Fprintf(fgcc, "#include <stdlib.h>\n")
|
||||
fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n\n")
|
||||
|
||||
|
@ -772,6 +769,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
|
|||
fmt.Fprintf(fgcc, "extern void _cgo_release_context(__SIZE_TYPE__);\n\n")
|
||||
fmt.Fprintf(fgcc, "extern char* _cgo_topofstack(void);")
|
||||
fmt.Fprintf(fgcc, "%s\n", tsanProlog)
|
||||
fmt.Fprintf(fgcc, "%s\n", msanProlog)
|
||||
|
||||
for _, exp := range p.ExpFunc {
|
||||
fn := exp.Func
|
||||
|
@ -1004,11 +1002,12 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) {
|
|||
|
||||
p.writeExportHeader(fgcch)
|
||||
|
||||
fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n")
|
||||
fmt.Fprintf(fgcc, "/* Code generated by cmd/cgo; DO NOT EDIT. */\n\n")
|
||||
fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n")
|
||||
|
||||
fmt.Fprintf(fgcc, "%s\n", gccgoExportFileProlog)
|
||||
fmt.Fprintf(fgcc, "%s\n", tsanProlog)
|
||||
fmt.Fprintf(fgcc, "%s\n", msanProlog)
|
||||
|
||||
for _, exp := range p.ExpFunc {
|
||||
fn := exp.Func
|
||||
|
@ -1170,7 +1169,7 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) {
|
|||
|
||||
// writeExportHeader writes out the start of the _cgo_export.h file.
|
||||
func (p *Package) writeExportHeader(fgcch io.Writer) {
|
||||
fmt.Fprintf(fgcch, "/* Created by \"go tool cgo\" - DO NOT EDIT. */\n\n")
|
||||
fmt.Fprintf(fgcch, "/* Code generated by cmd/cgo; DO NOT EDIT. */\n\n")
|
||||
pkg := *importPath
|
||||
if pkg == "" {
|
||||
pkg = p.PackagePath
|
||||
|
@ -1178,8 +1177,15 @@ func (p *Package) writeExportHeader(fgcch io.Writer) {
|
|||
fmt.Fprintf(fgcch, "/* package %s */\n\n", pkg)
|
||||
fmt.Fprintf(fgcch, "%s\n", builtinExportProlog)
|
||||
|
||||
// Remove absolute paths from #line comments in the preamble.
|
||||
// They aren't useful for people using the header file,
|
||||
// and they mean that the header files change based on the
|
||||
// exact location of GOPATH.
|
||||
re := regexp.MustCompile(`(?m)^(#line\s+[0-9]+\s+")[^"]*[/\\]([^"]*")`)
|
||||
preamble := re.ReplaceAllString(p.Preamble, "$1$2")
|
||||
|
||||
fmt.Fprintf(fgcch, "/* Start of preamble from import \"C\" comments. */\n\n")
|
||||
fmt.Fprintf(fgcch, "%s\n", p.Preamble)
|
||||
fmt.Fprintf(fgcch, "%s\n", preamble)
|
||||
fmt.Fprintf(fgcch, "\n/* End of preamble from import \"C\" comments. */\n\n")
|
||||
|
||||
fmt.Fprintf(fgcch, "%s\n", p.gccExportHeaderProlog())
|
||||
|
@ -1414,6 +1420,25 @@ static void _cgo_tsan_release() {
|
|||
// Set to yesTsanProlog if we see -fsanitize=thread in the flags for gcc.
|
||||
var tsanProlog = noTsanProlog
|
||||
|
||||
// noMsanProlog is a prologue defining an MSAN function in C.
|
||||
// This is used when not compiling with -fsanitize=memory.
|
||||
const noMsanProlog = `
|
||||
#define _cgo_msan_write(addr, sz)
|
||||
`
|
||||
|
||||
// yesMsanProlog is a prologue defining an MSAN function in C.
|
||||
// This is used when compiling with -fsanitize=memory.
|
||||
// See the comment above where _cgo_msan_write is called.
|
||||
const yesMsanProlog = `
|
||||
extern void __msan_unpoison(const volatile void *, size_t);
|
||||
|
||||
#define _cgo_msan_write(addr, sz) __msan_unpoison((addr), (sz))
|
||||
`
|
||||
|
||||
// msanProlog is set to yesMsanProlog if we see -fsanitize=memory in the flags
|
||||
// for the C compiler.
|
||||
var msanProlog = noMsanProlog
|
||||
|
||||
const builtinProlog = `
|
||||
#line 1 "cgo-builtin-prolog"
|
||||
#include <stddef.h> /* for ptrdiff_t and size_t below */
|
||||
|
|
|
@ -59,6 +59,8 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) {
|
|||
var bout, berr bytes.Buffer
|
||||
p.Stdout = &bout
|
||||
p.Stderr = &berr
|
||||
// Disable escape codes in clang error messages.
|
||||
p.Env = append(os.Environ(), "TERM=dumb")
|
||||
err := p.Run()
|
||||
if _, ok := err.(*exec.ExitError); err != nil && !ok {
|
||||
fatalf("%s", err)
|
||||
|
@ -97,6 +99,8 @@ func error_(pos token.Pos, msg string, args ...interface{}) {
|
|||
nerrors++
|
||||
if pos.IsValid() {
|
||||
fmt.Fprintf(os.Stderr, "%s: ", fset.Position(pos).String())
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "cgo: ")
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, msg, args...)
|
||||
fmt.Fprintf(os.Stderr, "\n")
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -12,7 +12,6 @@ import (
|
|||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -57,15 +56,6 @@ func TestAbsolutePath(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func isWindowsXP(t *testing.T) bool {
|
||||
v, err := syscall.GetVersion()
|
||||
if err != nil {
|
||||
t.Fatalf("GetVersion failed: %v", err)
|
||||
}
|
||||
major := byte(v)
|
||||
return major < 6
|
||||
}
|
||||
|
||||
func runIcacls(t *testing.T, args ...string) string {
|
||||
t.Helper()
|
||||
out, err := exec.Command("icacls", args...).CombinedOutput()
|
||||
|
@ -89,10 +79,6 @@ func runGetACL(t *testing.T, path string) string {
|
|||
// has discretionary access control list (DACL) set as if the file
|
||||
// was created in the destination directory.
|
||||
func TestACL(t *testing.T) {
|
||||
if isWindowsXP(t) {
|
||||
t.Skip("Windows XP does not have powershell command")
|
||||
}
|
||||
|
||||
tmpdir, err := ioutil.TempDir("", "TestACL")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -111,7 +97,7 @@ func TestACL(t *testing.T) {
|
|||
// will make all files created in TestACL/tmp have different
|
||||
// security attributes to the files created in TestACL.
|
||||
runIcacls(t, newtmpdir,
|
||||
"/grant", "guest:(oi)(ci)f", // add Guest user to have full access
|
||||
"/grant", "*S-1-5-32-546:(oi)(ci)f", // add Guests group to have full access
|
||||
)
|
||||
|
||||
src := filepath.Join(tmpdir, "main.go")
|
||||
|
|
|
@ -45,25 +45,43 @@ type Command struct {
|
|||
// CustomFlags indicates that the command will do its own
|
||||
// flag parsing.
|
||||
CustomFlags bool
|
||||
|
||||
// Commands lists the available commands and help topics.
|
||||
// The order here is the order in which they are printed by 'go help'.
|
||||
// Note that subcommands are in general best avoided.
|
||||
Commands []*Command
|
||||
}
|
||||
|
||||
// Commands lists the available commands and help topics.
|
||||
// The order here is the order in which they are printed by 'go help'.
|
||||
var Commands []*Command
|
||||
var Go = &Command{
|
||||
UsageLine: "go",
|
||||
Long: `Go is a tool for managing Go source code.`,
|
||||
// Commands initialized in package main
|
||||
}
|
||||
|
||||
// Name returns the command's name: the first word in the usage line.
|
||||
func (c *Command) Name() string {
|
||||
// LongName returns the command's long name: all the words in the usage line between "go" and a flag or argument,
|
||||
func (c *Command) LongName() string {
|
||||
name := c.UsageLine
|
||||
i := strings.Index(name, " ")
|
||||
if i >= 0 {
|
||||
if i := strings.Index(name, " ["); i >= 0 {
|
||||
name = name[:i]
|
||||
}
|
||||
if name == "go" {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimPrefix(name, "go ")
|
||||
}
|
||||
|
||||
// Name returns the command's short name: the last word in the usage line before a flag or argument.
|
||||
func (c *Command) Name() string {
|
||||
name := c.LongName()
|
||||
if i := strings.LastIndex(name, " "); i >= 0 {
|
||||
name = name[i+1:]
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func (c *Command) Usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: %s\n", c.UsageLine)
|
||||
fmt.Fprintf(os.Stderr, "Run 'go help %s' for details.\n", c.Name())
|
||||
fmt.Fprintf(os.Stderr, "Run 'go help %s' for details.\n", c.LongName())
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
|
|
152
libgo/go/cmd/go/internal/base/goflags.go
Normal file
152
libgo/go/cmd/go/internal/base/goflags.go
Normal file
|
@ -0,0 +1,152 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package base
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"cmd/go/internal/cfg"
|
||||
)
|
||||
|
||||
var (
|
||||
goflags []string // cached $GOFLAGS list; can be -x or --x form
|
||||
knownFlag = make(map[string]bool) // flags allowed to appear in $GOFLAGS; no leading dashes
|
||||
)
|
||||
|
||||
// AddKnownFlag adds name to the list of known flags for use in $GOFLAGS.
|
||||
func AddKnownFlag(name string) {
|
||||
knownFlag[name] = true
|
||||
}
|
||||
|
||||
// GOFLAGS returns the flags from $GOFLAGS.
|
||||
// The list can be assumed to contain one string per flag,
|
||||
// with each string either beginning with -name or --name.
|
||||
func GOFLAGS() []string {
|
||||
InitGOFLAGS()
|
||||
return goflags
|
||||
}
|
||||
|
||||
// InitGOFLAGS initializes the goflags list from $GOFLAGS.
|
||||
// If goflags is already initialized, it does nothing.
|
||||
func InitGOFLAGS() {
|
||||
if goflags != nil { // already initialized
|
||||
return
|
||||
}
|
||||
|
||||
// Build list of all flags for all commands.
|
||||
// If no command has that flag, then we report the problem.
|
||||
// This catches typos while still letting users record flags in GOFLAGS
|
||||
// that only apply to a subset of go commands.
|
||||
// Commands using CustomFlags can report their flag names
|
||||
// by calling AddKnownFlag instead.
|
||||
var walkFlags func(*Command)
|
||||
walkFlags = func(cmd *Command) {
|
||||
for _, sub := range cmd.Commands {
|
||||
walkFlags(sub)
|
||||
}
|
||||
cmd.Flag.VisitAll(func(f *flag.Flag) {
|
||||
knownFlag[f.Name] = true
|
||||
})
|
||||
}
|
||||
walkFlags(Go)
|
||||
|
||||
// Ignore bad flag in go env and go bug, because
|
||||
// they are what people reach for when debugging
|
||||
// a problem, and maybe they're debugging GOFLAGS.
|
||||
// (Both will show the GOFLAGS setting if let succeed.)
|
||||
hideErrors := cfg.CmdName == "env" || cfg.CmdName == "bug"
|
||||
|
||||
goflags = strings.Fields(os.Getenv("GOFLAGS"))
|
||||
if goflags == nil {
|
||||
goflags = []string{} // avoid work on later InitGOFLAGS call
|
||||
}
|
||||
|
||||
// Each of the words returned by strings.Fields must be its own flag.
|
||||
// To set flag arguments use -x=value instead of -x value.
|
||||
// For boolean flags, -x is fine instead of -x=true.
|
||||
for _, f := range goflags {
|
||||
// Check that every flag looks like -x --x -x=value or --x=value.
|
||||
if !strings.HasPrefix(f, "-") || f == "-" || f == "--" || strings.HasPrefix(f, "---") || strings.HasPrefix(f, "-=") || strings.HasPrefix(f, "--=") {
|
||||
if hideErrors {
|
||||
continue
|
||||
}
|
||||
Fatalf("go: parsing $GOFLAGS: non-flag %q", f)
|
||||
}
|
||||
|
||||
name := f[1:]
|
||||
if name[0] == '-' {
|
||||
name = name[1:]
|
||||
}
|
||||
if i := strings.Index(name, "="); i >= 0 {
|
||||
name = name[:i]
|
||||
}
|
||||
if !knownFlag[name] {
|
||||
if hideErrors {
|
||||
continue
|
||||
}
|
||||
Fatalf("go: parsing $GOFLAGS: unknown flag -%s", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// boolFlag is the optional interface for flag.Value known to the flag package.
|
||||
// (It is not clear why package flag does not export this interface.)
|
||||
type boolFlag interface {
|
||||
flag.Value
|
||||
IsBoolFlag() bool
|
||||
}
|
||||
|
||||
// SetFromGOFLAGS sets the flags in the given flag set using settings in $GOFLAGS.
|
||||
func SetFromGOFLAGS(flags flag.FlagSet) {
|
||||
InitGOFLAGS()
|
||||
|
||||
// This loop is similar to flag.Parse except that it ignores
|
||||
// unknown flags found in goflags, so that setting, say, GOFLAGS=-ldflags=-w
|
||||
// does not break commands that don't have a -ldflags.
|
||||
// It also adjusts the output to be clear that the reported problem is from $GOFLAGS.
|
||||
where := "$GOFLAGS"
|
||||
if runtime.GOOS == "windows" {
|
||||
where = "%GOFLAGS%"
|
||||
}
|
||||
for _, goflag := range goflags {
|
||||
name, value, hasValue := goflag, "", false
|
||||
if i := strings.Index(goflag, "="); i >= 0 {
|
||||
name, value, hasValue = goflag[:i], goflag[i+1:], true
|
||||
}
|
||||
if strings.HasPrefix(name, "--") {
|
||||
name = name[1:]
|
||||
}
|
||||
f := flags.Lookup(name[1:])
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
if fb, ok := f.Value.(boolFlag); ok && fb.IsBoolFlag() {
|
||||
if hasValue {
|
||||
if err := fb.Set(value); err != nil {
|
||||
fmt.Fprintf(flags.Output(), "go: invalid boolean value %q for flag %s (from %s): %v\n", value, name, where, err)
|
||||
flags.Usage()
|
||||
}
|
||||
} else {
|
||||
if err := fb.Set("true"); err != nil {
|
||||
fmt.Fprintf(flags.Output(), "go: invalid boolean flag %s (from %s): %v\n", name, where, err)
|
||||
flags.Usage()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !hasValue {
|
||||
fmt.Fprintf(flags.Output(), "go: flag needs an argument: %s (from %s)\n", name, where)
|
||||
flags.Usage()
|
||||
}
|
||||
if err := f.Value.Set(value); err != nil {
|
||||
fmt.Fprintf(flags.Output(), "go: invalid value %q for flag %s (from %s): %v\n", value, name, where, err)
|
||||
flags.Usage()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris
|
||||
// +build aix darwin dragonfly freebsd js linux nacl netbsd openbsd solaris
|
||||
|
||||
package base
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ import (
|
|||
|
||||
var CmdBug = &base.Command{
|
||||
Run: runBug,
|
||||
UsageLine: "bug",
|
||||
UsageLine: "go bug",
|
||||
Short: "start a bug report",
|
||||
Long: `
|
||||
Bug opens the default browser and starts a new bug report.
|
||||
|
@ -38,6 +38,9 @@ func init() {
|
|||
}
|
||||
|
||||
func runBug(cmd *base.Command, args []string) {
|
||||
if len(args) > 0 {
|
||||
base.Fatalf("go bug: bug takes no arguments")
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(bugHeader)
|
||||
inspectGoVersion(&buf)
|
||||
|
|
15
libgo/go/cmd/go/internal/cache/cache.go
vendored
15
libgo/go/cmd/go/internal/cache/cache.go
vendored
|
@ -189,6 +189,21 @@ func (c *Cache) get(id ActionID) (Entry, error) {
|
|||
return Entry{buf, size, time.Unix(0, tm)}, nil
|
||||
}
|
||||
|
||||
// GetFile looks up the action ID in the cache and returns
|
||||
// the name of the corresponding data file.
|
||||
func (c *Cache) GetFile(id ActionID) (file string, entry Entry, err error) {
|
||||
entry, err = c.Get(id)
|
||||
if err != nil {
|
||||
return "", Entry{}, err
|
||||
}
|
||||
file = c.OutputFile(entry.OutputID)
|
||||
info, err := os.Stat(file)
|
||||
if err != nil || info.Size() != entry.Size {
|
||||
return "", Entry{}, errMissing
|
||||
}
|
||||
return file, entry, nil
|
||||
}
|
||||
|
||||
// GetBytes looks up the action ID in the cache and returns
|
||||
// the corresponding output bytes.
|
||||
// GetBytes should only be used for data that can be expected to fit in memory.
|
||||
|
|
37
libgo/go/cmd/go/internal/cache/default.go
vendored
37
libgo/go/cmd/go/internal/cache/default.go
vendored
|
@ -35,12 +35,14 @@ See golang.org to learn more about Go.
|
|||
// initDefaultCache does the work of finding the default cache
|
||||
// the first time Default is called.
|
||||
func initDefaultCache() {
|
||||
dir := DefaultDir()
|
||||
dir, showWarnings := defaultDir()
|
||||
if dir == "off" {
|
||||
return
|
||||
}
|
||||
if err := os.MkdirAll(dir, 0777); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err)
|
||||
if showWarnings {
|
||||
fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(dir, "README")); err != nil {
|
||||
|
@ -50,7 +52,9 @@ func initDefaultCache() {
|
|||
|
||||
c, err := Open(dir)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err)
|
||||
if showWarnings {
|
||||
fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
defaultCache = c
|
||||
|
@ -59,14 +63,24 @@ func initDefaultCache() {
|
|||
// DefaultDir returns the effective GOCACHE setting.
|
||||
// It returns "off" if the cache is disabled.
|
||||
func DefaultDir() string {
|
||||
dir, _ := defaultDir()
|
||||
return dir
|
||||
}
|
||||
|
||||
// defaultDir returns the effective GOCACHE setting.
|
||||
// It returns "off" if the cache is disabled.
|
||||
// The second return value reports whether warnings should
|
||||
// be shown if the cache fails to initialize.
|
||||
func defaultDir() (string, bool) {
|
||||
dir := os.Getenv("GOCACHE")
|
||||
if dir != "" {
|
||||
return dir
|
||||
return dir, true
|
||||
}
|
||||
|
||||
// Compute default location.
|
||||
// TODO(rsc): This code belongs somewhere else,
|
||||
// like maybe ioutil.CacheDir or os.CacheDir.
|
||||
showWarnings := true
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
dir = os.Getenv("LocalAppData")
|
||||
|
@ -76,20 +90,20 @@ func DefaultDir() string {
|
|||
dir = os.Getenv("AppData")
|
||||
}
|
||||
if dir == "" {
|
||||
return "off"
|
||||
return "off", true
|
||||
}
|
||||
|
||||
case "darwin":
|
||||
dir = os.Getenv("HOME")
|
||||
if dir == "" {
|
||||
return "off"
|
||||
return "off", true
|
||||
}
|
||||
dir += "/Library/Caches"
|
||||
|
||||
case "plan9":
|
||||
dir = os.Getenv("home")
|
||||
if dir == "" {
|
||||
return "off"
|
||||
return "off", true
|
||||
}
|
||||
// Plan 9 has no established per-user cache directory,
|
||||
// but $home/lib/xyz is the usual equivalent of $HOME/.xyz on Unix.
|
||||
|
@ -101,10 +115,15 @@ func DefaultDir() string {
|
|||
if dir == "" {
|
||||
dir = os.Getenv("HOME")
|
||||
if dir == "" {
|
||||
return "off"
|
||||
return "off", true
|
||||
}
|
||||
if dir == "/" {
|
||||
// probably docker run with -u flag
|
||||
// https://golang.org/issue/26280
|
||||
showWarnings = false
|
||||
}
|
||||
dir += "/.cache"
|
||||
}
|
||||
}
|
||||
return filepath.Join(dir, "go-build")
|
||||
return filepath.Join(dir, "go-build"), showWarnings
|
||||
}
|
||||
|
|
67
libgo/go/cmd/go/internal/cache/default_unix_test.go
vendored
Normal file
67
libgo/go/cmd/go/internal/cache/default_unix_test.go
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !windows,!darwin,!plan9
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDefaultDir(t *testing.T) {
|
||||
goCacheDir := "/tmp/test-go-cache"
|
||||
xdgCacheDir := "/tmp/test-xdg-cache"
|
||||
homeDir := "/tmp/test-home"
|
||||
|
||||
// undo env changes when finished
|
||||
defer func(GOCACHE, XDG_CACHE_HOME, HOME string) {
|
||||
os.Setenv("GOCACHE", GOCACHE)
|
||||
os.Setenv("XDG_CACHE_HOME", XDG_CACHE_HOME)
|
||||
os.Setenv("HOME", HOME)
|
||||
}(os.Getenv("GOCACHE"), os.Getenv("XDG_CACHE_HOME"), os.Getenv("HOME"))
|
||||
|
||||
os.Setenv("GOCACHE", goCacheDir)
|
||||
os.Setenv("XDG_CACHE_HOME", xdgCacheDir)
|
||||
os.Setenv("HOME", homeDir)
|
||||
|
||||
dir, showWarnings := defaultDir()
|
||||
if dir != goCacheDir {
|
||||
t.Errorf("Cache DefaultDir %q should be $GOCACHE %q", dir, goCacheDir)
|
||||
}
|
||||
if !showWarnings {
|
||||
t.Error("Warnings should be shown when $GOCACHE is set")
|
||||
}
|
||||
|
||||
os.Unsetenv("GOCACHE")
|
||||
dir, showWarnings = defaultDir()
|
||||
if !strings.HasPrefix(dir, xdgCacheDir+"/") {
|
||||
t.Errorf("Cache DefaultDir %q should be under $XDG_CACHE_HOME %q when $GOCACHE is unset", dir, xdgCacheDir)
|
||||
}
|
||||
if !showWarnings {
|
||||
t.Error("Warnings should be shown when $XDG_CACHE_HOME is set")
|
||||
}
|
||||
|
||||
os.Unsetenv("XDG_CACHE_HOME")
|
||||
dir, showWarnings = defaultDir()
|
||||
if !strings.HasPrefix(dir, homeDir+"/.cache/") {
|
||||
t.Errorf("Cache DefaultDir %q should be under $HOME/.cache %q when $GOCACHE and $XDG_CACHE_HOME are unset", dir, homeDir+"/.cache")
|
||||
}
|
||||
if !showWarnings {
|
||||
t.Error("Warnings should be shown when $HOME is not /")
|
||||
}
|
||||
|
||||
os.Unsetenv("HOME")
|
||||
if dir, _ := defaultDir(); dir != "off" {
|
||||
t.Error("Cache not disabled when $GOCACHE, $XDG_CACHE_HOME, and $HOME are unset")
|
||||
}
|
||||
|
||||
os.Setenv("HOME", "/")
|
||||
if _, showWarnings := defaultDir(); showWarnings {
|
||||
// https://golang.org/issue/26280
|
||||
t.Error("Cache initalization warnings should be squelched when $GOCACHE and $XDG_CACHE_HOME are unset and $HOME is /")
|
||||
}
|
||||
}
|
|
@ -20,7 +20,8 @@ import (
|
|||
var (
|
||||
BuildA bool // -a flag
|
||||
BuildBuildmode string // -buildmode flag
|
||||
BuildContext = build.Default
|
||||
BuildContext = defaultContext()
|
||||
BuildMod string // -mod flag
|
||||
BuildI bool // -i flag
|
||||
BuildLinkshared bool // -linkshared flag
|
||||
BuildMSan bool // -msan flag
|
||||
|
@ -42,6 +43,12 @@ var (
|
|||
DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable)
|
||||
)
|
||||
|
||||
func defaultContext() build.Context {
|
||||
ctxt := build.Default
|
||||
ctxt.JoinPath = filepath.Join // back door to say "do not use go command"
|
||||
return ctxt
|
||||
}
|
||||
|
||||
func init() {
|
||||
BuildToolchainCompiler = func() string { return "missing-compiler" }
|
||||
BuildToolchainLinker = func() string { return "missing-linker" }
|
||||
|
@ -67,6 +74,16 @@ var (
|
|||
Goos = BuildContext.GOOS
|
||||
ExeSuffix string
|
||||
Gopath = filepath.SplitList(BuildContext.GOPATH)
|
||||
|
||||
// ModulesEnabled specifies whether the go command is running
|
||||
// in module-aware mode (as opposed to GOPATH mode).
|
||||
// It is equal to modload.Enabled, but not all packages can import modload.
|
||||
ModulesEnabled bool
|
||||
|
||||
// GoModInGOPATH records whether we've found a go.mod in GOPATH/src
|
||||
// in GO111MODULE=auto mode. In that case, we don't use modules
|
||||
// but people might expect us to, so 'go get' warns.
|
||||
GoModInGOPATH string
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -84,9 +101,10 @@ var (
|
|||
GOROOT_FINAL = findGOROOT_FINAL()
|
||||
|
||||
// Used in envcmd.MkEnv and build ID computations.
|
||||
GOARM = fmt.Sprint(objabi.GOARM)
|
||||
GO386 = objabi.GO386
|
||||
GOMIPS = objabi.GOMIPS
|
||||
GOARM = fmt.Sprint(objabi.GOARM)
|
||||
GO386 = objabi.GO386
|
||||
GOMIPS = objabi.GOMIPS
|
||||
GOMIPS64 = objabi.GOMIPS64
|
||||
)
|
||||
|
||||
// Update build context to use our computed GOROOT.
|
||||
|
@ -102,6 +120,16 @@ func init() {
|
|||
}
|
||||
}
|
||||
|
||||
// There is a copy of findGOROOT, isSameDir, and isGOROOT in
|
||||
// x/tools/cmd/godoc/goroot.go.
|
||||
// Try to keep them in sync for now.
|
||||
|
||||
// findGOROOT returns the GOROOT value, using either an explicitly
|
||||
// provided environment variable, a GOROOT that contains the current
|
||||
// os.Executable value, or else the GOROOT that the binary was built
|
||||
// with from runtime.GOROOT().
|
||||
//
|
||||
// There is a copy of this code in x/tools/cmd/godoc/goroot.go.
|
||||
func findGOROOT() string {
|
||||
if env := os.Getenv("GOROOT"); env != "" {
|
||||
return filepath.Clean(env)
|
||||
|
@ -161,6 +189,8 @@ func isSameDir(dir1, dir2 string) bool {
|
|||
// It does this by looking for the path/pkg/tool directory,
|
||||
// which is necessary for useful operation of the cmd/go tool,
|
||||
// and is not typically present in a GOPATH.
|
||||
//
|
||||
// There is a copy of this code in x/tools/cmd/godoc/goroot.go.
|
||||
func isGOROOT(path string) bool {
|
||||
stat, err := os.Stat(filepath.Join(path, "pkg", "tool"))
|
||||
if err != nil {
|
||||
|
|
|
@ -17,11 +17,13 @@ import (
|
|||
"cmd/go/internal/cache"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/load"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/work"
|
||||
)
|
||||
|
||||
var CmdClean = &base.Command{
|
||||
UsageLine: "clean [-i] [-r] [-n] [-x] [-cache] [-testcache] [build flags] [packages]",
|
||||
UsageLine: "go clean [clean flags] [build flags] [packages]",
|
||||
Short: "remove object files and cached files",
|
||||
Long: `
|
||||
Clean removes object files from package source directories.
|
||||
|
@ -65,6 +67,10 @@ The -cache flag causes clean to remove the entire go build cache.
|
|||
The -testcache flag causes clean to expire all test results in the
|
||||
go build cache.
|
||||
|
||||
The -modcache flag causes clean to remove the entire module
|
||||
download cache, including unpacked source code of versioned
|
||||
dependencies.
|
||||
|
||||
For more about build flags, see 'go help build'.
|
||||
|
||||
For more about specifying packages, see 'go help packages'.
|
||||
|
@ -75,6 +81,7 @@ var (
|
|||
cleanI bool // clean -i flag
|
||||
cleanR bool // clean -r flag
|
||||
cleanCache bool // clean -cache flag
|
||||
cleanModcache bool // clean -modcache flag
|
||||
cleanTestcache bool // clean -testcache flag
|
||||
)
|
||||
|
||||
|
@ -85,6 +92,7 @@ func init() {
|
|||
CmdClean.Flag.BoolVar(&cleanI, "i", false, "")
|
||||
CmdClean.Flag.BoolVar(&cleanR, "r", false, "")
|
||||
CmdClean.Flag.BoolVar(&cleanCache, "cache", false, "")
|
||||
CmdClean.Flag.BoolVar(&cleanModcache, "modcache", false, "")
|
||||
CmdClean.Flag.BoolVar(&cleanTestcache, "testcache", false, "")
|
||||
|
||||
// -n and -x are important enough to be
|
||||
|
@ -95,8 +103,13 @@ func init() {
|
|||
}
|
||||
|
||||
func runClean(cmd *base.Command, args []string) {
|
||||
for _, pkg := range load.PackagesAndErrors(args) {
|
||||
clean(pkg)
|
||||
if len(args) == 0 && modload.Failed() {
|
||||
// Don't try to clean current directory,
|
||||
// which will cause modload to base.Fatalf.
|
||||
} else {
|
||||
for _, pkg := range load.PackagesAndErrors(args) {
|
||||
clean(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
if cleanCache {
|
||||
|
@ -138,6 +151,29 @@ func runClean(cmd *base.Command, args []string) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cleanModcache {
|
||||
if modfetch.PkgMod == "" {
|
||||
base.Fatalf("go clean -modcache: no module cache")
|
||||
}
|
||||
if err := removeAll(modfetch.PkgMod); err != nil {
|
||||
base.Errorf("go clean -modcache: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeAll(dir string) error {
|
||||
// Module cache has 0555 directories; make them writable in order to remove content.
|
||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil // ignore errors walking in file system
|
||||
}
|
||||
if info.IsDir() {
|
||||
os.Chmod(path, 0777)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return os.RemoveAll(dir)
|
||||
}
|
||||
|
||||
var cleaned = map[*load.Package]bool{}
|
||||
|
|
|
@ -69,6 +69,14 @@ func SyntaxError(cmd, msg string) {
|
|||
os.Exit(2)
|
||||
}
|
||||
|
||||
// AddKnownFlags registers the flags in defns with base.AddKnownFlag.
|
||||
func AddKnownFlags(cmd string, defns []*Defn) {
|
||||
for _, f := range defns {
|
||||
base.AddKnownFlag(f.Name)
|
||||
base.AddKnownFlag(cmd + "." + f.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Parse sees if argument i is present in the definitions and if so,
|
||||
// returns its definition, value, and whether it consumed an extra word.
|
||||
// If the flag begins (cmd+".") it is ignored for the purpose of this function.
|
||||
|
@ -121,3 +129,31 @@ func Parse(cmd string, defns []*Defn, args []string, i int) (f *Defn, value stri
|
|||
f = nil
|
||||
return
|
||||
}
|
||||
|
||||
// FindGOFLAGS extracts and returns the flags matching defns from GOFLAGS.
|
||||
// Ideally the caller would mention that the flags were from GOFLAGS
|
||||
// when reporting errors, but that's too hard for now.
|
||||
func FindGOFLAGS(defns []*Defn) []string {
|
||||
var flags []string
|
||||
for _, flag := range base.GOFLAGS() {
|
||||
// Flags returned by base.GOFLAGS are well-formed, one of:
|
||||
// -x
|
||||
// --x
|
||||
// -x=value
|
||||
// --x=value
|
||||
if strings.HasPrefix(flag, "--") {
|
||||
flag = flag[1:]
|
||||
}
|
||||
name := flag[1:]
|
||||
if i := strings.Index(name, "="); i >= 0 {
|
||||
name = name[:i]
|
||||
}
|
||||
for _, f := range defns {
|
||||
if name == f.Name {
|
||||
flags = append(flags, flag)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
|
103
libgo/go/cmd/go/internal/dirhash/hash.go
Normal file
103
libgo/go/cmd/go/internal/dirhash/hash.go
Normal file
|
@ -0,0 +1,103 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package dirhash defines hashes over directory trees.
|
||||
package dirhash
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var DefaultHash = Hash1
|
||||
|
||||
type Hash func(files []string, open func(string) (io.ReadCloser, error)) (string, error)
|
||||
|
||||
func Hash1(files []string, open func(string) (io.ReadCloser, error)) (string, error) {
|
||||
h := sha256.New()
|
||||
files = append([]string(nil), files...)
|
||||
sort.Strings(files)
|
||||
for _, file := range files {
|
||||
if strings.Contains(file, "\n") {
|
||||
return "", errors.New("filenames with newlines are not supported")
|
||||
}
|
||||
r, err := open(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
hf := sha256.New()
|
||||
_, err = io.Copy(hf, r)
|
||||
r.Close()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
fmt.Fprintf(h, "%x %s\n", hf.Sum(nil), file)
|
||||
}
|
||||
return "h1:" + base64.StdEncoding.EncodeToString(h.Sum(nil)), nil
|
||||
}
|
||||
|
||||
func HashDir(dir, prefix string, hash Hash) (string, error) {
|
||||
files, err := DirFiles(dir, prefix)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
osOpen := func(name string) (io.ReadCloser, error) {
|
||||
return os.Open(filepath.Join(dir, strings.TrimPrefix(name, prefix)))
|
||||
}
|
||||
return hash(files, osOpen)
|
||||
}
|
||||
|
||||
func DirFiles(dir, prefix string) ([]string, error) {
|
||||
var files []string
|
||||
dir = filepath.Clean(dir)
|
||||
err := filepath.Walk(dir, func(file string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
rel := file
|
||||
if dir != "." {
|
||||
rel = file[len(dir)+1:]
|
||||
}
|
||||
f := filepath.Join(prefix, rel)
|
||||
files = append(files, filepath.ToSlash(f))
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func HashZip(zipfile string, hash Hash) (string, error) {
|
||||
z, err := zip.OpenReader(zipfile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer z.Close()
|
||||
var files []string
|
||||
zfiles := make(map[string]*zip.File)
|
||||
for _, file := range z.File {
|
||||
files = append(files, file.Name)
|
||||
zfiles[file.Name] = file
|
||||
}
|
||||
zipOpen := func(name string) (io.ReadCloser, error) {
|
||||
f := zfiles[name]
|
||||
if f == nil {
|
||||
return nil, fmt.Errorf("file %q not found in zip", name) // should never happen
|
||||
}
|
||||
return f.Open()
|
||||
}
|
||||
return hash(files, zipOpen)
|
||||
}
|
135
libgo/go/cmd/go/internal/dirhash/hash_test.go
Normal file
135
libgo/go/cmd/go/internal/dirhash/hash_test.go
Normal file
|
@ -0,0 +1,135 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dirhash
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func h(s string) string {
|
||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(s)))
|
||||
}
|
||||
|
||||
func htop(k string, s string) string {
|
||||
sum := sha256.Sum256([]byte(s))
|
||||
return k + ":" + base64.StdEncoding.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func TestHash1(t *testing.T) {
|
||||
files := []string{"xyz", "abc"}
|
||||
open := func(name string) (io.ReadCloser, error) {
|
||||
return ioutil.NopCloser(strings.NewReader("data for " + name)), nil
|
||||
}
|
||||
want := htop("h1", fmt.Sprintf("%s %s\n%s %s\n", h("data for abc"), "abc", h("data for xyz"), "xyz"))
|
||||
out, err := Hash1(files, open)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if out != want {
|
||||
t.Errorf("Hash1(...) = %s, want %s", out, want)
|
||||
}
|
||||
|
||||
_, err = Hash1([]string{"xyz", "a\nbc"}, open)
|
||||
if err == nil {
|
||||
t.Error("Hash1: expected error on newline in filenames")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashDir(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "dirhash-test-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
if err := ioutil.WriteFile(filepath.Join(dir, "xyz"), []byte("data for xyz"), 0666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("data for abc"), 0666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := htop("h1", fmt.Sprintf("%s %s\n%s %s\n", h("data for abc"), "prefix/abc", h("data for xyz"), "prefix/xyz"))
|
||||
out, err := HashDir(dir, "prefix", Hash1)
|
||||
if err != nil {
|
||||
t.Fatalf("HashDir: %v", err)
|
||||
}
|
||||
if out != want {
|
||||
t.Errorf("HashDir(...) = %s, want %s", out, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashZip(t *testing.T) {
|
||||
f, err := ioutil.TempFile("", "dirhash-test-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
defer f.Close()
|
||||
|
||||
z := zip.NewWriter(f)
|
||||
w, err := z.Create("prefix/xyz")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
w.Write([]byte("data for xyz"))
|
||||
w, err = z.Create("prefix/abc")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
w.Write([]byte("data for abc"))
|
||||
if err := z.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
want := htop("h1", fmt.Sprintf("%s %s\n%s %s\n", h("data for abc"), "prefix/abc", h("data for xyz"), "prefix/xyz"))
|
||||
out, err := HashZip(f.Name(), Hash1)
|
||||
if err != nil {
|
||||
t.Fatalf("HashDir: %v", err)
|
||||
}
|
||||
if out != want {
|
||||
t.Errorf("HashDir(...) = %s, want %s", out, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDirFiles(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "dirfiles-test-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
if err := ioutil.WriteFile(filepath.Join(dir, "xyz"), []byte("data for xyz"), 0666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("data for abc"), 0666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Mkdir(filepath.Join(dir, "subdir"), 0777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ioutil.WriteFile(filepath.Join(dir, "subdir", "xyz"), []byte("data for subdir xyz"), 0666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
prefix := "foo/bar@v2.3.4"
|
||||
out, err := DirFiles(dir, prefix)
|
||||
if err != nil {
|
||||
t.Fatalf("DirFiles: %v", err)
|
||||
}
|
||||
for _, file := range out {
|
||||
if !strings.HasPrefix(file, prefix) {
|
||||
t.Errorf("Dir file = %s, want prefix %s", file, prefix)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
var CmdDoc = &base.Command{
|
||||
Run: runDoc,
|
||||
UsageLine: "doc [-u] [-c] [package|[package.]symbol[.methodOrField]]",
|
||||
UsageLine: "go doc [-u] [-c] [package|[package.]symbol[.methodOrField]]",
|
||||
CustomFlags: true,
|
||||
Short: "show documentation for package or symbol",
|
||||
Long: `
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
|
@ -16,11 +17,12 @@ import (
|
|||
"cmd/go/internal/cache"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/load"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/work"
|
||||
)
|
||||
|
||||
var CmdEnv = &base.Command{
|
||||
UsageLine: "env [-json] [var ...]",
|
||||
UsageLine: "go env [-json] [var ...]",
|
||||
Short: "print Go environment information",
|
||||
Long: `
|
||||
Env prints Go environment information.
|
||||
|
@ -52,17 +54,16 @@ func MkEnv() []cfg.EnvVar {
|
|||
{Name: "GOBIN", Value: cfg.GOBIN},
|
||||
{Name: "GOCACHE", Value: cache.DefaultDir()},
|
||||
{Name: "GOEXE", Value: cfg.ExeSuffix},
|
||||
{Name: "GOFLAGS", Value: os.Getenv("GOFLAGS")},
|
||||
{Name: "GOHOSTARCH", Value: runtime.GOARCH},
|
||||
{Name: "GOHOSTOS", Value: runtime.GOOS},
|
||||
{Name: "GOOS", Value: cfg.Goos},
|
||||
{Name: "GOPATH", Value: cfg.BuildContext.GOPATH},
|
||||
{Name: "GOPROXY", Value: os.Getenv("GOPROXY")},
|
||||
{Name: "GORACE", Value: os.Getenv("GORACE")},
|
||||
{Name: "GOROOT", Value: cfg.GOROOT},
|
||||
{Name: "GOTMPDIR", Value: os.Getenv("GOTMPDIR")},
|
||||
{Name: "GOTOOLDIR", Value: base.ToolDir},
|
||||
|
||||
// disable escape codes in clang errors
|
||||
{Name: "TERM", Value: "dumb"},
|
||||
}
|
||||
|
||||
if work.GccgoBin != "" {
|
||||
|
@ -78,6 +79,8 @@ func MkEnv() []cfg.EnvVar {
|
|||
env = append(env, cfg.EnvVar{Name: "GO386", Value: cfg.GO386})
|
||||
case "mips", "mipsle":
|
||||
env = append(env, cfg.EnvVar{Name: "GOMIPS", Value: cfg.GOMIPS})
|
||||
case "mips64", "mips64le":
|
||||
env = append(env, cfg.EnvVar{Name: "GOMIPS64", Value: cfg.GOMIPS64})
|
||||
}
|
||||
|
||||
cc := cfg.DefaultCC(cfg.Goos, cfg.Goarch)
|
||||
|
@ -111,6 +114,18 @@ func findEnv(env []cfg.EnvVar, name string) string {
|
|||
|
||||
// ExtraEnvVars returns environment variables that should not leak into child processes.
|
||||
func ExtraEnvVars() []cfg.EnvVar {
|
||||
gomod := ""
|
||||
if modload.Init(); modload.ModRoot != "" {
|
||||
gomod = filepath.Join(modload.ModRoot, "go.mod")
|
||||
}
|
||||
return []cfg.EnvVar{
|
||||
{Name: "GOMOD", Value: gomod},
|
||||
}
|
||||
}
|
||||
|
||||
// ExtraEnvVarsCostly returns environment variables that should not leak into child processes
|
||||
// but are costly to evaluate.
|
||||
func ExtraEnvVarsCostly() []cfg.EnvVar {
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
cppflags, cflags, cxxflags, fflags, ldflags, err := b.CFlags(&load.Package{})
|
||||
|
@ -120,6 +135,7 @@ func ExtraEnvVars() []cfg.EnvVar {
|
|||
return nil
|
||||
}
|
||||
cmd := b.GccCmd(".", "")
|
||||
|
||||
return []cfg.EnvVar{
|
||||
// Note: Update the switch in runEnv below when adding to this list.
|
||||
{Name: "CGO_CFLAGS", Value: strings.Join(cflags, " ")},
|
||||
|
@ -134,13 +150,14 @@ func ExtraEnvVars() []cfg.EnvVar {
|
|||
|
||||
func runEnv(cmd *base.Command, args []string) {
|
||||
env := cfg.CmdEnv
|
||||
env = append(env, ExtraEnvVars()...)
|
||||
|
||||
// Do we need to call ExtraEnvVars, which is a bit expensive?
|
||||
// Do we need to call ExtraEnvVarsCostly, which is a bit expensive?
|
||||
// Only if we're listing all environment variables ("go env")
|
||||
// or the variables being requested are in the extra list.
|
||||
needExtra := true
|
||||
needCostly := true
|
||||
if len(args) > 0 {
|
||||
needExtra = false
|
||||
needCostly = false
|
||||
for _, arg := range args {
|
||||
switch arg {
|
||||
case "CGO_CFLAGS",
|
||||
|
@ -150,12 +167,12 @@ func runEnv(cmd *base.Command, args []string) {
|
|||
"CGO_LDFLAGS",
|
||||
"PKG_CONFIG",
|
||||
"GOGCCFLAGS":
|
||||
needExtra = true
|
||||
needCostly = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if needExtra {
|
||||
env = append(env, ExtraEnvVars()...)
|
||||
if needCostly {
|
||||
env = append(env, ExtraEnvVarsCostly()...)
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
|
|
|
@ -9,12 +9,15 @@ import (
|
|||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/load"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/str"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var CmdFix = &base.Command{
|
||||
Run: runFix,
|
||||
UsageLine: "fix [packages]",
|
||||
UsageLine: "go fix [packages]",
|
||||
Short: "update packages to use new APIs",
|
||||
Long: `
|
||||
Fix runs the Go fix command on the packages named by the import paths.
|
||||
|
@ -29,7 +32,15 @@ See also: go fmt, go vet.
|
|||
}
|
||||
|
||||
func runFix(cmd *base.Command, args []string) {
|
||||
printed := false
|
||||
for _, pkg := range load.Packages(args) {
|
||||
if modload.Enabled() && !pkg.Module.Main {
|
||||
if !printed {
|
||||
fmt.Fprintf(os.Stderr, "go: not fixing packages in dependency modules\n")
|
||||
printed = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
// Use pkg.gofiles instead of pkg.Dir so that
|
||||
// the command only applies to this package,
|
||||
// not to packages in subdirectories.
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package fmtcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
@ -15,6 +16,7 @@ import (
|
|||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/load"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/str"
|
||||
)
|
||||
|
||||
|
@ -24,7 +26,7 @@ func init() {
|
|||
|
||||
var CmdFmt = &base.Command{
|
||||
Run: runFmt,
|
||||
UsageLine: "fmt [-n] [-x] [packages]",
|
||||
UsageLine: "go fmt [-n] [-x] [packages]",
|
||||
Short: "gofmt (reformat) package sources",
|
||||
Long: `
|
||||
Fmt runs the command 'gofmt -l -w' on the packages named
|
||||
|
@ -43,6 +45,7 @@ See also: go fix, go vet.
|
|||
}
|
||||
|
||||
func runFmt(cmd *base.Command, args []string) {
|
||||
printed := false
|
||||
gofmt := gofmtPath()
|
||||
procs := runtime.GOMAXPROCS(0)
|
||||
var wg sync.WaitGroup
|
||||
|
@ -57,6 +60,13 @@ func runFmt(cmd *base.Command, args []string) {
|
|||
}()
|
||||
}
|
||||
for _, pkg := range load.PackagesAndErrors(args) {
|
||||
if modload.Enabled() && pkg.Module != nil && !pkg.Module.Main {
|
||||
if !printed {
|
||||
fmt.Fprintf(os.Stderr, "go: not formatting packages in dependency modules\n")
|
||||
printed = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
if pkg.Error != nil {
|
||||
if strings.HasPrefix(pkg.Error.Err, "build constraints exclude all Go files") {
|
||||
// Skip this error, as we will format
|
||||
|
|
|
@ -21,12 +21,13 @@ import (
|
|||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/load"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/work"
|
||||
)
|
||||
|
||||
var CmdGenerate = &base.Command{
|
||||
Run: runGenerate,
|
||||
UsageLine: "generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]",
|
||||
UsageLine: "go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]",
|
||||
Short: "generate Go files by processing source",
|
||||
Long: `
|
||||
Generate runs commands described by directives within existing
|
||||
|
@ -47,6 +48,12 @@ that can be run locally. It must either be in the shell path
|
|||
(gofmt), a fully qualified path (/usr/you/bin/mytool), or a
|
||||
command alias, described below.
|
||||
|
||||
To convey to humans and machine tools that code is generated,
|
||||
generated source should have a line early in the file that
|
||||
matches the following regular expression (in Go syntax):
|
||||
|
||||
^// Code generated .* DO NOT EDIT\.$
|
||||
|
||||
Note that go generate does not parse the file, so lines that look
|
||||
like directives in comments or multiline strings will be treated
|
||||
as directives.
|
||||
|
@ -152,9 +159,28 @@ func runGenerate(cmd *base.Command, args []string) {
|
|||
}
|
||||
}
|
||||
// Even if the arguments are .go files, this loop suffices.
|
||||
printed := false
|
||||
for _, pkg := range load.Packages(args) {
|
||||
if modload.Enabled() && !pkg.Module.Main {
|
||||
if !printed {
|
||||
fmt.Fprintf(os.Stderr, "go: not generating in packages in dependency modules\n")
|
||||
printed = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
pkgName := pkg.Name
|
||||
|
||||
for _, file := range pkg.InternalGoFiles() {
|
||||
if !generate(pkg.Name, file) {
|
||||
if !generate(pkgName, file) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
pkgName += "_test"
|
||||
|
||||
for _, file := range pkg.InternalXGoFiles() {
|
||||
if !generate(pkgName, file) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ func charsetReader(charset string, input io.Reader) (io.Reader, error) {
|
|||
|
||||
// parseMetaGoImports returns meta imports from the HTML in r.
|
||||
// Parsing ends at the end of the <head> section or the beginning of the <body>.
|
||||
func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) {
|
||||
func parseMetaGoImports(r io.Reader, mod ModuleMode) (imports []metaImport, err error) {
|
||||
d := xml.NewDecoder(r)
|
||||
d.CharsetReader = charsetReader
|
||||
d.Strict = false
|
||||
|
@ -39,13 +39,13 @@ func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) {
|
|||
if err == io.EOF || len(imports) > 0 {
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
break
|
||||
}
|
||||
if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") {
|
||||
return
|
||||
break
|
||||
}
|
||||
if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") {
|
||||
return
|
||||
break
|
||||
}
|
||||
e, ok := t.(xml.StartElement)
|
||||
if !ok || !strings.EqualFold(e.Name.Local, "meta") {
|
||||
|
@ -55,13 +55,6 @@ func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) {
|
|||
continue
|
||||
}
|
||||
if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 {
|
||||
// Ignore VCS type "mod", which is new Go modules.
|
||||
// This code is for old go get and must ignore the new mod lines.
|
||||
// Otherwise matchGoImport will complain about two
|
||||
// different metaImport lines for the same Prefix.
|
||||
if f[1] == "mod" {
|
||||
continue
|
||||
}
|
||||
imports = append(imports, metaImport{
|
||||
Prefix: f[0],
|
||||
VCS: f[1],
|
||||
|
@ -69,6 +62,27 @@ func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Extract mod entries if we are paying attention to them.
|
||||
var list []metaImport
|
||||
var have map[string]bool
|
||||
if mod == PreferMod {
|
||||
have = make(map[string]bool)
|
||||
for _, m := range imports {
|
||||
if m.VCS == "mod" {
|
||||
have[m.Prefix] = true
|
||||
list = append(list, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Append non-mod entries, ignoring those superseded by a mod entry.
|
||||
for _, m := range imports {
|
||||
if m.VCS != "mod" && !have[m.Prefix] {
|
||||
list = append(list, m)
|
||||
}
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// attrValue returns the attribute value for the case-insensitive key
|
||||
|
|
|
@ -16,13 +16,14 @@ import (
|
|||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/load"
|
||||
"cmd/go/internal/search"
|
||||
"cmd/go/internal/str"
|
||||
"cmd/go/internal/web"
|
||||
"cmd/go/internal/work"
|
||||
)
|
||||
|
||||
var CmdGet = &base.Command{
|
||||
UsageLine: "get [-d] [-f] [-fix] [-insecure] [-t] [-u] [-v] [build flags] [packages]",
|
||||
UsageLine: "go get [-d] [-f] [-t] [-u] [-v] [-fix] [-insecure] [build flags] [packages]",
|
||||
Short: "download and install packages and dependencies",
|
||||
Long: `
|
||||
Get downloads the packages named by the import paths, along with their
|
||||
|
@ -73,23 +74,56 @@ For more about specifying packages, see 'go help packages'.
|
|||
For more about how 'go get' finds source code to
|
||||
download, see 'go help importpath'.
|
||||
|
||||
This text describes the behavior of get when using GOPATH
|
||||
to manage source code and dependencies.
|
||||
If instead the go command is running in module-aware mode,
|
||||
the details of get's flags and effects change, as does 'go help get'.
|
||||
See 'go help modules' and 'go help module-get'.
|
||||
|
||||
See also: go build, go install, go clean.
|
||||
`,
|
||||
}
|
||||
|
||||
var getD = CmdGet.Flag.Bool("d", false, "")
|
||||
var getF = CmdGet.Flag.Bool("f", false, "")
|
||||
var getT = CmdGet.Flag.Bool("t", false, "")
|
||||
var getU = CmdGet.Flag.Bool("u", false, "")
|
||||
var getFix = CmdGet.Flag.Bool("fix", false, "")
|
||||
var getInsecure = CmdGet.Flag.Bool("insecure", false, "")
|
||||
var HelpGopathGet = &base.Command{
|
||||
UsageLine: "gopath-get",
|
||||
Short: "legacy GOPATH go get",
|
||||
Long: `
|
||||
The 'go get' command changes behavior depending on whether the
|
||||
go command is running in module-aware mode or legacy GOPATH mode.
|
||||
This help text, accessible as 'go help gopath-get' even in module-aware mode,
|
||||
describes 'go get' as it operates in legacy GOPATH mode.
|
||||
|
||||
Usage: ` + CmdGet.UsageLine + `
|
||||
` + CmdGet.Long,
|
||||
}
|
||||
|
||||
var (
|
||||
getD = CmdGet.Flag.Bool("d", false, "")
|
||||
getF = CmdGet.Flag.Bool("f", false, "")
|
||||
getT = CmdGet.Flag.Bool("t", false, "")
|
||||
getU = CmdGet.Flag.Bool("u", false, "")
|
||||
getFix = CmdGet.Flag.Bool("fix", false, "")
|
||||
|
||||
Insecure bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
work.AddBuildFlags(CmdGet)
|
||||
CmdGet.Run = runGet // break init loop
|
||||
CmdGet.Flag.BoolVar(&Insecure, "insecure", Insecure, "")
|
||||
}
|
||||
|
||||
func runGet(cmd *base.Command, args []string) {
|
||||
if cfg.ModulesEnabled {
|
||||
// Should not happen: main.go should install the separate module-enabled get code.
|
||||
base.Fatalf("go get: modules not implemented")
|
||||
}
|
||||
if cfg.GoModInGOPATH != "" {
|
||||
// Warn about not using modules with GO111MODULE=auto when go.mod exists.
|
||||
// To silence the warning, users can set GO111MODULE=off.
|
||||
fmt.Fprintf(os.Stderr, "go get: warning: modules disabled by GO111MODULE=auto in GOPATH/src;\n\tignoring %s;\n\tsee 'go help modules'\n", base.ShortPath(cfg.GoModInGOPATH))
|
||||
}
|
||||
|
||||
work.BuildInit()
|
||||
|
||||
if *getF && !*getU {
|
||||
|
@ -129,9 +163,8 @@ func runGet(cmd *base.Command, args []string) {
|
|||
if *getT {
|
||||
mode |= load.GetTestDeps
|
||||
}
|
||||
args = downloadPaths(args)
|
||||
for _, arg := range args {
|
||||
download(arg, nil, &stk, mode)
|
||||
for _, pkg := range downloadPaths(args) {
|
||||
download(pkg, nil, &stk, mode)
|
||||
}
|
||||
base.ExitIfErrors()
|
||||
|
||||
|
@ -150,8 +183,7 @@ func runGet(cmd *base.Command, args []string) {
|
|||
// This leads to duplicated loads of the standard packages.
|
||||
load.ClearCmdCache()
|
||||
|
||||
args = load.ImportPaths(args)
|
||||
load.PackagesForBuild(args)
|
||||
pkgs := load.PackagesForBuild(args)
|
||||
|
||||
// Phase 3. Install.
|
||||
if *getD {
|
||||
|
@ -161,7 +193,7 @@ func runGet(cmd *base.Command, args []string) {
|
|||
return
|
||||
}
|
||||
|
||||
work.InstallPackages(args, true)
|
||||
work.InstallPackages(args, pkgs)
|
||||
}
|
||||
|
||||
// downloadPaths prepares the list of paths to pass to download.
|
||||
|
@ -169,28 +201,21 @@ func runGet(cmd *base.Command, args []string) {
|
|||
// for a particular pattern, downloadPaths leaves it in the result list,
|
||||
// in the hope that we can figure out the repository from the
|
||||
// initial ...-free prefix.
|
||||
func downloadPaths(args []string) []string {
|
||||
args = load.ImportPathsNoDotExpansion(args)
|
||||
var out []string
|
||||
for _, a := range args {
|
||||
if strings.Contains(a, "...") {
|
||||
var expand []string
|
||||
// Use matchPackagesInFS to avoid printing
|
||||
// warnings. They will be printed by the
|
||||
// eventual call to importPaths instead.
|
||||
if build.IsLocalImport(a) {
|
||||
expand = load.MatchPackagesInFS(a)
|
||||
} else {
|
||||
expand = load.MatchPackages(a)
|
||||
}
|
||||
if len(expand) > 0 {
|
||||
out = append(out, expand...)
|
||||
continue
|
||||
}
|
||||
func downloadPaths(patterns []string) []string {
|
||||
for _, arg := range patterns {
|
||||
if strings.Contains(arg, "@") {
|
||||
base.Fatalf("go: cannot use path@version syntax in GOPATH mode")
|
||||
}
|
||||
out = append(out, a)
|
||||
}
|
||||
return out
|
||||
var pkgs []string
|
||||
for _, m := range search.ImportPathsQuiet(patterns) {
|
||||
if len(m.Pkgs) == 0 && strings.Contains(m.Pattern, "...") {
|
||||
pkgs = append(pkgs, m.Pattern)
|
||||
} else {
|
||||
pkgs = append(pkgs, m.Pkgs...)
|
||||
}
|
||||
}
|
||||
return pkgs
|
||||
}
|
||||
|
||||
// downloadCache records the import paths we have already
|
||||
|
@ -215,7 +240,7 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int)
|
|||
}
|
||||
load1 := func(path string, mode int) *load.Package {
|
||||
if parent == nil {
|
||||
return load.LoadPackage(path, stk)
|
||||
return load.LoadPackageNoFlags(path, stk)
|
||||
}
|
||||
return load.LoadImport(path, parent.Dir, parent, stk, nil, mode|load.ResolveModule)
|
||||
}
|
||||
|
@ -271,9 +296,9 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int)
|
|||
// for p has been replaced in the package cache.
|
||||
if wildcardOkay && strings.Contains(arg, "...") {
|
||||
if build.IsLocalImport(arg) {
|
||||
args = load.MatchPackagesInFS(arg)
|
||||
args = search.MatchPackagesInFS(arg).Pkgs
|
||||
} else {
|
||||
args = load.MatchPackages(arg)
|
||||
args = search.MatchPackages(arg).Pkgs
|
||||
}
|
||||
isWildcard = true
|
||||
}
|
||||
|
@ -304,7 +329,7 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int)
|
|||
base.Run(cfg.BuildToolexec, str.StringList(base.Tool("fix"), files))
|
||||
|
||||
// The imports might have changed, so reload again.
|
||||
p = load.ReloadPackage(arg, stk)
|
||||
p = load.ReloadPackageNoFlags(arg, stk)
|
||||
if p.Error != nil {
|
||||
base.Errorf("%s", p.Error)
|
||||
return
|
||||
|
@ -369,10 +394,11 @@ func downloadPackage(p *load.Package) error {
|
|||
vcs *vcsCmd
|
||||
repo, rootPath string
|
||||
err error
|
||||
blindRepo bool // set if the repo has unusual configuration
|
||||
)
|
||||
|
||||
security := web.Secure
|
||||
if *getInsecure {
|
||||
if Insecure {
|
||||
security = web.Insecure
|
||||
}
|
||||
|
||||
|
@ -389,20 +415,22 @@ func downloadPackage(p *load.Package) error {
|
|||
dir := filepath.Join(p.Internal.Build.SrcRoot, filepath.FromSlash(rootPath))
|
||||
remote, err := vcs.remoteRepo(vcs, dir)
|
||||
if err != nil {
|
||||
return err
|
||||
// Proceed anyway. The package is present; we likely just don't understand
|
||||
// the repo configuration (e.g. unusual remote protocol).
|
||||
blindRepo = true
|
||||
}
|
||||
repo = remote
|
||||
if !*getF {
|
||||
if rr, err := repoRootForImportPath(p.ImportPath, security); err == nil {
|
||||
repo := rr.repo
|
||||
if !*getF && err == nil {
|
||||
if rr, err := RepoRootForImportPath(p.ImportPath, IgnoreMod, security); err == nil {
|
||||
repo := rr.Repo
|
||||
if rr.vcs.resolveRepo != nil {
|
||||
resolved, err := rr.vcs.resolveRepo(rr.vcs, dir, repo)
|
||||
if err == nil {
|
||||
repo = resolved
|
||||
}
|
||||
}
|
||||
if remote != repo && rr.isCustom {
|
||||
return fmt.Errorf("%s is a custom import path for %s, but %s is checked out from %s", rr.root, repo, dir, remote)
|
||||
if remote != repo && rr.IsCustom {
|
||||
return fmt.Errorf("%s is a custom import path for %s, but %s is checked out from %s", rr.Root, repo, dir, remote)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -410,13 +438,13 @@ func downloadPackage(p *load.Package) error {
|
|||
} else {
|
||||
// Analyze the import path to determine the version control system,
|
||||
// repository, and the import path for the root of the repository.
|
||||
rr, err := repoRootForImportPath(p.ImportPath, security)
|
||||
rr, err := RepoRootForImportPath(p.ImportPath, IgnoreMod, security)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
vcs, repo, rootPath = rr.vcs, rr.repo, rr.root
|
||||
vcs, repo, rootPath = rr.vcs, rr.Repo, rr.Root
|
||||
}
|
||||
if !vcs.isSecure(repo) && !*getInsecure {
|
||||
if !blindRepo && !vcs.isSecure(repo) && !Insecure {
|
||||
return fmt.Errorf("cannot download, %v uses insecure protocol", repo)
|
||||
}
|
||||
|
||||
|
|
|
@ -33,15 +33,18 @@ func TestFoldDup(t *testing.T) {
|
|||
|
||||
var parseMetaGoImportsTests = []struct {
|
||||
in string
|
||||
mod ModuleMode
|
||||
out []metaImport
|
||||
}{
|
||||
{
|
||||
`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
|
||||
IgnoreMod,
|
||||
[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
|
||||
},
|
||||
{
|
||||
`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
|
||||
<meta name="go-import" content="baz/quux git http://github.com/rsc/baz/quux">`,
|
||||
IgnoreMod,
|
||||
[]metaImport{
|
||||
{"foo/bar", "git", "https://github.com/rsc/foo/bar"},
|
||||
{"baz/quux", "git", "http://github.com/rsc/baz/quux"},
|
||||
|
@ -50,6 +53,7 @@ var parseMetaGoImportsTests = []struct {
|
|||
{
|
||||
`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
|
||||
<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">`,
|
||||
IgnoreMod,
|
||||
[]metaImport{
|
||||
{"foo/bar", "git", "https://github.com/rsc/foo/bar"},
|
||||
},
|
||||
|
@ -57,35 +61,65 @@ var parseMetaGoImportsTests = []struct {
|
|||
{
|
||||
`<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">
|
||||
<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
|
||||
IgnoreMod,
|
||||
[]metaImport{
|
||||
{"foo/bar", "git", "https://github.com/rsc/foo/bar"},
|
||||
},
|
||||
},
|
||||
{
|
||||
`<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">
|
||||
<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
|
||||
PreferMod,
|
||||
[]metaImport{
|
||||
{"foo/bar", "mod", "http://github.com/rsc/baz/quux"},
|
||||
},
|
||||
},
|
||||
{
|
||||
`<head>
|
||||
<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
|
||||
</head>`,
|
||||
IgnoreMod,
|
||||
[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
|
||||
},
|
||||
{
|
||||
`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
|
||||
<body>`,
|
||||
IgnoreMod,
|
||||
[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
|
||||
},
|
||||
{
|
||||
`<!doctype html><meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
|
||||
IgnoreMod,
|
||||
[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
|
||||
},
|
||||
{
|
||||
// XML doesn't like <div style=position:relative>.
|
||||
`<!doctype html><title>Page Not Found</title><meta name=go-import content="chitin.io/chitin git https://github.com/chitin-io/chitin"><div style=position:relative>DRAFT</div>`,
|
||||
IgnoreMod,
|
||||
[]metaImport{{"chitin.io/chitin", "git", "https://github.com/chitin-io/chitin"}},
|
||||
},
|
||||
{
|
||||
`<meta name="go-import" content="myitcv.io git https://github.com/myitcv/x">
|
||||
<meta name="go-import" content="myitcv.io/blah2 mod https://raw.githubusercontent.com/myitcv/pubx/master">
|
||||
`,
|
||||
IgnoreMod,
|
||||
[]metaImport{{"myitcv.io", "git", "https://github.com/myitcv/x"}},
|
||||
},
|
||||
{
|
||||
`<meta name="go-import" content="myitcv.io git https://github.com/myitcv/x">
|
||||
<meta name="go-import" content="myitcv.io/blah2 mod https://raw.githubusercontent.com/myitcv/pubx/master">
|
||||
`,
|
||||
PreferMod,
|
||||
[]metaImport{
|
||||
{"myitcv.io/blah2", "mod", "https://raw.githubusercontent.com/myitcv/pubx/master"},
|
||||
{"myitcv.io", "git", "https://github.com/myitcv/x"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestParseMetaGoImports(t *testing.T) {
|
||||
for i, tt := range parseMetaGoImportsTests {
|
||||
out, err := parseMetaGoImports(strings.NewReader(tt.in))
|
||||
out, err := parseMetaGoImports(strings.NewReader(tt.in), tt.mod)
|
||||
if err != nil {
|
||||
t.Errorf("test#%d: %v", i, err)
|
||||
continue
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
package get
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -428,19 +427,18 @@ func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool)
|
|||
fmt.Printf("cd %s\n", dir)
|
||||
fmt.Printf("%s %s\n", v.cmd, strings.Join(args, " "))
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
cmd.Stdout = &buf
|
||||
cmd.Stderr = &buf
|
||||
err = cmd.Run()
|
||||
out := buf.Bytes()
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
if verbose || cfg.BuildV {
|
||||
fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " "))
|
||||
os.Stderr.Write(out)
|
||||
if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 {
|
||||
os.Stderr.Write(ee.Stderr)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, err.Error())
|
||||
}
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
return out, nil
|
||||
return out, err
|
||||
}
|
||||
|
||||
// ping pings to determine scheme to use.
|
||||
|
@ -624,27 +622,29 @@ func checkNestedVCS(vcs *vcsCmd, dir, srcRoot string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// repoRoot represents a version control system, a repo, and a root of
|
||||
// where to put it on disk.
|
||||
type repoRoot struct {
|
||||
vcs *vcsCmd
|
||||
// RepoRoot describes the repository root for a tree of source code.
|
||||
type RepoRoot struct {
|
||||
Repo string // repository URL, including scheme
|
||||
Root string // import path corresponding to root of repo
|
||||
IsCustom bool // defined by served <meta> tags (as opposed to hard-coded pattern)
|
||||
VCS string // vcs type ("mod", "git", ...)
|
||||
|
||||
// repo is the repository URL, including scheme
|
||||
repo string
|
||||
|
||||
// root is the import path corresponding to the root of the
|
||||
// repository
|
||||
root string
|
||||
|
||||
// isCustom is true for custom import paths (those defined by HTML meta tags)
|
||||
isCustom bool
|
||||
vcs *vcsCmd // internal: vcs command access
|
||||
}
|
||||
|
||||
var httpPrefixRE = regexp.MustCompile(`^https?:`)
|
||||
|
||||
// repoRootForImportPath analyzes importPath to determine the
|
||||
// ModuleMode specifies whether to prefer modules when looking up code sources.
|
||||
type ModuleMode int
|
||||
|
||||
const (
|
||||
IgnoreMod ModuleMode = iota
|
||||
PreferMod
|
||||
)
|
||||
|
||||
// RepoRootForImportPath analyzes importPath to determine the
|
||||
// version control system, and code repository to use.
|
||||
func repoRootForImportPath(importPath string, security web.SecurityMode) (*repoRoot, error) {
|
||||
func RepoRootForImportPath(importPath string, mod ModuleMode, security web.SecurityMode) (*RepoRoot, error) {
|
||||
rr, err := repoRootFromVCSPaths(importPath, "", security, vcsPaths)
|
||||
if err == errUnknownSite {
|
||||
// If there are wildcards, look up the thing before the wildcard,
|
||||
|
@ -654,7 +654,7 @@ func repoRootForImportPath(importPath string, security web.SecurityMode) (*repoR
|
|||
if i := strings.Index(lookup, "/.../"); i >= 0 {
|
||||
lookup = lookup[:i]
|
||||
}
|
||||
rr, err = repoRootForImportDynamic(lookup, security)
|
||||
rr, err = repoRootForImportDynamic(lookup, mod, security)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unrecognized import path %q (%v)", importPath, err)
|
||||
}
|
||||
|
@ -667,7 +667,7 @@ func repoRootForImportPath(importPath string, security web.SecurityMode) (*repoR
|
|||
}
|
||||
}
|
||||
|
||||
if err == nil && strings.Contains(importPath, "...") && strings.Contains(rr.root, "...") {
|
||||
if err == nil && strings.Contains(importPath, "...") && strings.Contains(rr.Root, "...") {
|
||||
// Do not allow wildcards in the repo root.
|
||||
rr = nil
|
||||
err = fmt.Errorf("cannot expand ... in %q", importPath)
|
||||
|
@ -680,7 +680,7 @@ var errUnknownSite = errors.New("dynamic lookup required to find mapping")
|
|||
// repoRootFromVCSPaths attempts to map importPath to a repoRoot
|
||||
// using the mappings defined in vcsPaths.
|
||||
// If scheme is non-empty, that scheme is forced.
|
||||
func repoRootFromVCSPaths(importPath, scheme string, security web.SecurityMode, vcsPaths []*vcsPath) (*repoRoot, error) {
|
||||
func repoRootFromVCSPaths(importPath, scheme string, security web.SecurityMode, vcsPaths []*vcsPath) (*RepoRoot, error) {
|
||||
// A common error is to use https://packagepath because that's what
|
||||
// hg and git require. Diagnose this helpfully.
|
||||
if loc := httpPrefixRE.FindStringIndex(importPath); loc != nil {
|
||||
|
@ -733,28 +733,32 @@ func repoRootFromVCSPaths(importPath, scheme string, security web.SecurityMode,
|
|||
if security == web.Secure && !vcs.isSecureScheme(scheme) {
|
||||
continue
|
||||
}
|
||||
if vcs.ping(scheme, match["repo"]) == nil {
|
||||
if vcs.pingCmd != "" && vcs.ping(scheme, match["repo"]) == nil {
|
||||
match["repo"] = scheme + "://" + match["repo"]
|
||||
break
|
||||
goto Found
|
||||
}
|
||||
}
|
||||
// No scheme found. Fall back to the first one.
|
||||
match["repo"] = vcs.scheme[0] + "://" + match["repo"]
|
||||
Found:
|
||||
}
|
||||
}
|
||||
rr := &repoRoot{
|
||||
rr := &RepoRoot{
|
||||
Repo: match["repo"],
|
||||
Root: match["root"],
|
||||
VCS: vcs.cmd,
|
||||
vcs: vcs,
|
||||
repo: match["repo"],
|
||||
root: match["root"],
|
||||
}
|
||||
return rr, nil
|
||||
}
|
||||
return nil, errUnknownSite
|
||||
}
|
||||
|
||||
// repoRootForImportDynamic finds a *repoRoot for a custom domain that's not
|
||||
// repoRootForImportDynamic finds a *RepoRoot for a custom domain that's not
|
||||
// statically known by repoRootForImportPathStatic.
|
||||
//
|
||||
// This handles custom import paths like "name.tld/pkg/foo" or just "name.tld".
|
||||
func repoRootForImportDynamic(importPath string, security web.SecurityMode) (*repoRoot, error) {
|
||||
func repoRootForImportDynamic(importPath string, mod ModuleMode, security web.SecurityMode) (*RepoRoot, error) {
|
||||
slash := strings.Index(importPath, "/")
|
||||
if slash < 0 {
|
||||
slash = len(importPath)
|
||||
|
@ -772,7 +776,7 @@ func repoRootForImportDynamic(importPath string, security web.SecurityMode) (*re
|
|||
return nil, fmt.Errorf(msg, err)
|
||||
}
|
||||
defer body.Close()
|
||||
imports, err := parseMetaGoImports(body)
|
||||
imports, err := parseMetaGoImports(body, mod)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing %s: %v", importPath, err)
|
||||
}
|
||||
|
@ -799,7 +803,7 @@ func repoRootForImportDynamic(importPath string, security web.SecurityMode) (*re
|
|||
}
|
||||
urlStr0 := urlStr
|
||||
var imports []metaImport
|
||||
urlStr, imports, err = metaImportsForPrefix(mmi.Prefix, security)
|
||||
urlStr, imports, err = metaImportsForPrefix(mmi.Prefix, mod, security)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -809,48 +813,34 @@ func repoRootForImportDynamic(importPath string, security web.SecurityMode) (*re
|
|||
}
|
||||
}
|
||||
|
||||
if err := validateRepoRootScheme(mmi.RepoRoot); err != nil {
|
||||
if err := validateRepoRoot(mmi.RepoRoot); err != nil {
|
||||
return nil, fmt.Errorf("%s: invalid repo root %q: %v", urlStr, mmi.RepoRoot, err)
|
||||
}
|
||||
rr := &repoRoot{
|
||||
vcs: vcsByCmd(mmi.VCS),
|
||||
repo: mmi.RepoRoot,
|
||||
root: mmi.Prefix,
|
||||
isCustom: true,
|
||||
}
|
||||
if rr.vcs == nil {
|
||||
vcs := vcsByCmd(mmi.VCS)
|
||||
if vcs == nil && mmi.VCS != "mod" {
|
||||
return nil, fmt.Errorf("%s: unknown vcs %q", urlStr, mmi.VCS)
|
||||
}
|
||||
|
||||
rr := &RepoRoot{
|
||||
Repo: mmi.RepoRoot,
|
||||
Root: mmi.Prefix,
|
||||
IsCustom: true,
|
||||
VCS: mmi.VCS,
|
||||
vcs: vcs,
|
||||
}
|
||||
return rr, nil
|
||||
}
|
||||
|
||||
// validateRepoRootScheme returns an error if repoRoot does not seem
|
||||
// to have a valid URL scheme. At this point we permit things that
|
||||
// aren't valid URLs, although later, if not using -insecure, we will
|
||||
// restrict repoRoots to be valid URLs. This is only because we've
|
||||
// historically permitted them, and people may depend on that.
|
||||
func validateRepoRootScheme(repoRoot string) error {
|
||||
end := strings.Index(repoRoot, "://")
|
||||
if end <= 0 {
|
||||
// validateRepoRoot returns an error if repoRoot does not seem to be
|
||||
// a valid URL with scheme.
|
||||
func validateRepoRoot(repoRoot string) error {
|
||||
url, err := url.Parse(repoRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if url.Scheme == "" {
|
||||
return errors.New("no scheme")
|
||||
}
|
||||
|
||||
// RFC 3986 section 3.1.
|
||||
for i := 0; i < end; i++ {
|
||||
c := repoRoot[i]
|
||||
switch {
|
||||
case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
|
||||
// OK.
|
||||
case '0' <= c && c <= '9' || c == '+' || c == '-' || c == '.':
|
||||
// OK except at start.
|
||||
if i == 0 {
|
||||
return errors.New("invalid scheme")
|
||||
}
|
||||
default:
|
||||
return errors.New("invalid scheme")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -868,7 +858,7 @@ var (
|
|||
// It is an error if no imports are found.
|
||||
// urlStr will still be valid if err != nil.
|
||||
// The returned urlStr will be of the form "https://golang.org/x/tools?go-get=1"
|
||||
func metaImportsForPrefix(importPrefix string, security web.SecurityMode) (urlStr string, imports []metaImport, err error) {
|
||||
func metaImportsForPrefix(importPrefix string, mod ModuleMode, security web.SecurityMode) (urlStr string, imports []metaImport, err error) {
|
||||
setCache := func(res fetchResult) (fetchResult, error) {
|
||||
fetchCacheMu.Lock()
|
||||
defer fetchCacheMu.Unlock()
|
||||
|
@ -888,7 +878,7 @@ func metaImportsForPrefix(importPrefix string, security web.SecurityMode) (urlSt
|
|||
if err != nil {
|
||||
return setCache(fetchResult{urlStr: urlStr, err: fmt.Errorf("fetch %s: %v", urlStr, err)})
|
||||
}
|
||||
imports, err := parseMetaGoImports(body)
|
||||
imports, err := parseMetaGoImports(body, mod)
|
||||
if err != nil {
|
||||
return setCache(fetchResult{urlStr: urlStr, err: fmt.Errorf("parsing %s: %v", urlStr, err)})
|
||||
}
|
||||
|
@ -956,7 +946,13 @@ func matchGoImport(imports []metaImport, importPath string) (metaImport, error)
|
|||
continue
|
||||
}
|
||||
|
||||
if match != -1 {
|
||||
if match >= 0 {
|
||||
if imports[match].VCS == "mod" && im.VCS != "mod" {
|
||||
// All the mod entries precede all the non-mod entries.
|
||||
// We have a mod entry and don't care about the rest,
|
||||
// matching or not.
|
||||
break
|
||||
}
|
||||
return metaImport{}, fmt.Errorf("multiple meta tags match import path %q", importPath)
|
||||
}
|
||||
match = i
|
||||
|
@ -1001,7 +997,7 @@ var vcsPaths = []*vcsPath{
|
|||
// IBM DevOps Services (JazzHub)
|
||||
{
|
||||
prefix: "hub.jazz.net/git/",
|
||||
re: `^(?P<root>hub.jazz.net/git/[a-z0-9]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`,
|
||||
re: `^(?P<root>hub\.jazz\.net/git/[a-z0-9]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`,
|
||||
vcs: "git",
|
||||
repo: "https://{root}",
|
||||
check: noVCSSuffix,
|
||||
|
@ -1010,7 +1006,7 @@ var vcsPaths = []*vcsPath{
|
|||
// Git at Apache
|
||||
{
|
||||
prefix: "git.apache.org/",
|
||||
re: `^(?P<root>git.apache.org/[a-z0-9_.\-]+\.git)(/[A-Za-z0-9_.\-]+)*$`,
|
||||
re: `^(?P<root>git\.apache\.org/[a-z0-9_.\-]+\.git)(/[A-Za-z0-9_.\-]+)*$`,
|
||||
vcs: "git",
|
||||
repo: "https://{root}",
|
||||
},
|
||||
|
|
|
@ -16,49 +16,53 @@ import (
|
|||
"cmd/go/internal/web"
|
||||
)
|
||||
|
||||
// Test that RepoRootForImportPath creates the correct RepoRoot for a given importPath.
|
||||
// Test that RepoRootForImportPath determines the correct RepoRoot for a given importPath.
|
||||
// TODO(cmang): Add tests for SVN and BZR.
|
||||
func TestRepoRootForImportPath(t *testing.T) {
|
||||
testenv.MustHaveExternalNetwork(t)
|
||||
|
||||
tests := []struct {
|
||||
path string
|
||||
want *repoRoot
|
||||
want *RepoRoot
|
||||
}{
|
||||
{
|
||||
"github.com/golang/groupcache",
|
||||
&repoRoot{
|
||||
&RepoRoot{
|
||||
vcs: vcsGit,
|
||||
repo: "https://github.com/golang/groupcache",
|
||||
Repo: "https://github.com/golang/groupcache",
|
||||
},
|
||||
},
|
||||
// Unicode letters in directories (issue 18660).
|
||||
{
|
||||
"github.com/user/unicode/испытание",
|
||||
&repoRoot{
|
||||
&RepoRoot{
|
||||
vcs: vcsGit,
|
||||
repo: "https://github.com/user/unicode",
|
||||
Repo: "https://github.com/user/unicode",
|
||||
},
|
||||
},
|
||||
// IBM DevOps Services tests
|
||||
{
|
||||
"hub.jazz.net/git/user1/pkgname",
|
||||
&repoRoot{
|
||||
&RepoRoot{
|
||||
vcs: vcsGit,
|
||||
repo: "https://hub.jazz.net/git/user1/pkgname",
|
||||
Repo: "https://hub.jazz.net/git/user1/pkgname",
|
||||
},
|
||||
},
|
||||
{
|
||||
"hub.jazz.net/git/user1/pkgname/submodule/submodule/submodule",
|
||||
&repoRoot{
|
||||
&RepoRoot{
|
||||
vcs: vcsGit,
|
||||
repo: "https://hub.jazz.net/git/user1/pkgname",
|
||||
Repo: "https://hub.jazz.net/git/user1/pkgname",
|
||||
},
|
||||
},
|
||||
{
|
||||
"hub.jazz.net",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"hubajazz.net",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"hub2.jazz.net",
|
||||
nil,
|
||||
|
@ -87,9 +91,9 @@ func TestRepoRootForImportPath(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"hub.jazz.net/git/user/pkg.name",
|
||||
&repoRoot{
|
||||
&RepoRoot{
|
||||
vcs: vcsGit,
|
||||
repo: "https://hub.jazz.net/git/user/pkg.name",
|
||||
Repo: "https://hub.jazz.net/git/user/pkg.name",
|
||||
},
|
||||
},
|
||||
// User names cannot have uppercase letters
|
||||
|
@ -100,9 +104,9 @@ func TestRepoRootForImportPath(t *testing.T) {
|
|||
// OpenStack tests
|
||||
{
|
||||
"git.openstack.org/openstack/swift",
|
||||
&repoRoot{
|
||||
&RepoRoot{
|
||||
vcs: vcsGit,
|
||||
repo: "https://git.openstack.org/openstack/swift",
|
||||
Repo: "https://git.openstack.org/openstack/swift",
|
||||
},
|
||||
},
|
||||
// Trailing .git is less preferred but included for
|
||||
|
@ -110,16 +114,16 @@ func TestRepoRootForImportPath(t *testing.T) {
|
|||
// be compilable on both old and new go
|
||||
{
|
||||
"git.openstack.org/openstack/swift.git",
|
||||
&repoRoot{
|
||||
&RepoRoot{
|
||||
vcs: vcsGit,
|
||||
repo: "https://git.openstack.org/openstack/swift.git",
|
||||
Repo: "https://git.openstack.org/openstack/swift.git",
|
||||
},
|
||||
},
|
||||
{
|
||||
"git.openstack.org/openstack/swift/go/hummingbird",
|
||||
&repoRoot{
|
||||
&RepoRoot{
|
||||
vcs: vcsGit,
|
||||
repo: "https://git.openstack.org/openstack/swift",
|
||||
Repo: "https://git.openstack.org/openstack/swift",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -140,25 +144,29 @@ func TestRepoRootForImportPath(t *testing.T) {
|
|||
"git.apache.org/package-name/path/to/lib",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"gitbapache.org",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"git.apache.org/package-name.git",
|
||||
&repoRoot{
|
||||
&RepoRoot{
|
||||
vcs: vcsGit,
|
||||
repo: "https://git.apache.org/package-name.git",
|
||||
Repo: "https://git.apache.org/package-name.git",
|
||||
},
|
||||
},
|
||||
{
|
||||
"git.apache.org/package-name_2.x.git/path/to/lib",
|
||||
&repoRoot{
|
||||
&RepoRoot{
|
||||
vcs: vcsGit,
|
||||
repo: "https://git.apache.org/package-name_2.x.git",
|
||||
Repo: "https://git.apache.org/package-name_2.x.git",
|
||||
},
|
||||
},
|
||||
{
|
||||
"chiselapp.com/user/kyle/repository/fossilgg",
|
||||
&repoRoot{
|
||||
&RepoRoot{
|
||||
vcs: vcsFossil,
|
||||
repo: "https://chiselapp.com/user/kyle/repository/fossilgg",
|
||||
Repo: "https://chiselapp.com/user/kyle/repository/fossilgg",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -173,21 +181,21 @@ func TestRepoRootForImportPath(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, test := range tests {
|
||||
got, err := repoRootForImportPath(test.path, web.Secure)
|
||||
got, err := RepoRootForImportPath(test.path, IgnoreMod, web.Secure)
|
||||
want := test.want
|
||||
|
||||
if want == nil {
|
||||
if err == nil {
|
||||
t.Errorf("repoRootForImportPath(%q): Error expected but not received", test.path)
|
||||
t.Errorf("RepoRootForImportPath(%q): Error expected but not received", test.path)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("repoRootForImportPath(%q): %v", test.path, err)
|
||||
t.Errorf("RepoRootForImportPath(%q): %v", test.path, err)
|
||||
continue
|
||||
}
|
||||
if got.vcs.name != want.vcs.name || got.repo != want.repo {
|
||||
t.Errorf("repoRootForImportPath(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.vcs, got.repo, want.vcs, want.repo)
|
||||
if got.vcs.name != want.vcs.name || got.Repo != want.Repo {
|
||||
t.Errorf("RepoRootForImportPath(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.vcs, got.Repo, want.vcs, want.Repo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -219,18 +227,18 @@ func TestFromDir(t *testing.T) {
|
|||
f.Close()
|
||||
}
|
||||
|
||||
want := repoRoot{
|
||||
want := RepoRoot{
|
||||
vcs: vcs,
|
||||
root: path.Join("example.com", vcs.name),
|
||||
Root: path.Join("example.com", vcs.name),
|
||||
}
|
||||
var got repoRoot
|
||||
got.vcs, got.root, err = vcsFromDir(dir, tempDir)
|
||||
var got RepoRoot
|
||||
got.vcs, got.Root, err = vcsFromDir(dir, tempDir)
|
||||
if err != nil {
|
||||
t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err)
|
||||
continue
|
||||
}
|
||||
if got.vcs.name != want.vcs.name || got.root != want.root {
|
||||
t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.vcs, got.root, want.vcs, want.root)
|
||||
if got.vcs.name != want.vcs.name || got.Root != want.Root {
|
||||
t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.vcs, got.Root, want.vcs, want.Root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -393,6 +401,22 @@ func TestMatchGoImport(t *testing.T) {
|
|||
path: "different.example.com/user/foo",
|
||||
err: errors.New("meta tags do not match import path"),
|
||||
},
|
||||
{
|
||||
imports: []metaImport{
|
||||
{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
|
||||
{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
|
||||
},
|
||||
path: "myitcv.io/blah2/foo",
|
||||
mi: metaImport{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
|
||||
},
|
||||
{
|
||||
imports: []metaImport{
|
||||
{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
|
||||
{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
|
||||
},
|
||||
path: "myitcv.io/other",
|
||||
mi: metaImport{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
@ -409,45 +433,46 @@ func TestMatchGoImport(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestValidateRepoRootScheme(t *testing.T) {
|
||||
func TestValidateRepoRoot(t *testing.T) {
|
||||
tests := []struct {
|
||||
root string
|
||||
err string
|
||||
ok bool
|
||||
}{
|
||||
{
|
||||
root: "",
|
||||
err: "no scheme",
|
||||
ok: false,
|
||||
},
|
||||
{
|
||||
root: "http://",
|
||||
err: "",
|
||||
ok: true,
|
||||
},
|
||||
{
|
||||
root: "a://",
|
||||
err: "",
|
||||
root: "git+ssh://",
|
||||
ok: true,
|
||||
},
|
||||
{
|
||||
root: "a#://",
|
||||
err: "invalid scheme",
|
||||
root: "http#://",
|
||||
ok: false,
|
||||
},
|
||||
{
|
||||
root: "-config",
|
||||
ok: false,
|
||||
},
|
||||
{
|
||||
root: "-config://",
|
||||
err: "invalid scheme",
|
||||
ok: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := validateRepoRootScheme(test.root)
|
||||
if err == nil {
|
||||
if test.err != "" {
|
||||
t.Errorf("validateRepoRootScheme(%q) = nil, want %q", test.root, test.err)
|
||||
err := validateRepoRoot(test.root)
|
||||
ok := err == nil
|
||||
if ok != test.ok {
|
||||
want := "error"
|
||||
if test.ok {
|
||||
want = "nil"
|
||||
}
|
||||
} else if test.err == "" {
|
||||
if err != nil {
|
||||
t.Errorf("validateRepoRootScheme(%q) = %q, want nil", test.root, test.err)
|
||||
}
|
||||
} else if err.Error() != test.err {
|
||||
t.Errorf("validateRepoRootScheme(%q) = %q, want %q", test.root, err, test.err)
|
||||
t.Errorf("validateRepoRoot(%q) = %q, want %s", test.root, err, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,82 +21,95 @@ import (
|
|||
|
||||
// Help implements the 'help' command.
|
||||
func Help(args []string) {
|
||||
if len(args) == 0 {
|
||||
PrintUsage(os.Stdout)
|
||||
// not exit 2: succeeded at 'go help'.
|
||||
return
|
||||
}
|
||||
if len(args) != 1 {
|
||||
fmt.Fprintf(os.Stderr, "usage: go help command\n\nToo many arguments given.\n")
|
||||
os.Exit(2) // failed at 'go help'
|
||||
}
|
||||
|
||||
arg := args[0]
|
||||
|
||||
// 'go help documentation' generates doc.go.
|
||||
if arg == "documentation" {
|
||||
if len(args) == 1 && args[0] == "documentation" {
|
||||
fmt.Println("// Copyright 2011 The Go Authors. All rights reserved.")
|
||||
fmt.Println("// Use of this source code is governed by a BSD-style")
|
||||
fmt.Println("// license that can be found in the LICENSE file.")
|
||||
fmt.Println()
|
||||
fmt.Println("// DO NOT EDIT THIS FILE. GENERATED BY mkalldocs.sh.")
|
||||
fmt.Println("// Code generated by mkalldocs.sh; DO NOT EDIT.")
|
||||
fmt.Println("// Edit the documentation in other files and rerun mkalldocs.sh to generate this one.")
|
||||
fmt.Println()
|
||||
buf := new(bytes.Buffer)
|
||||
PrintUsage(buf)
|
||||
PrintUsage(buf, base.Go)
|
||||
usage := &base.Command{Long: buf.String()}
|
||||
tmpl(&commentWriter{W: os.Stdout}, documentationTemplate, append([]*base.Command{usage}, base.Commands...))
|
||||
cmds := []*base.Command{usage}
|
||||
for _, cmd := range base.Go.Commands {
|
||||
if cmd.UsageLine == "gopath-get" {
|
||||
// Avoid duplication of the "get" documentation.
|
||||
continue
|
||||
}
|
||||
cmds = append(cmds, cmd)
|
||||
cmds = append(cmds, cmd.Commands...)
|
||||
}
|
||||
tmpl(&commentWriter{W: os.Stdout}, documentationTemplate, cmds)
|
||||
fmt.Println("package main")
|
||||
return
|
||||
}
|
||||
|
||||
for _, cmd := range base.Commands {
|
||||
if cmd.Name() == arg {
|
||||
tmpl(os.Stdout, helpTemplate, cmd)
|
||||
// not exit 2: succeeded at 'go help cmd'.
|
||||
return
|
||||
cmd := base.Go
|
||||
Args:
|
||||
for i, arg := range args {
|
||||
for _, sub := range cmd.Commands {
|
||||
if sub.Name() == arg {
|
||||
cmd = sub
|
||||
continue Args
|
||||
}
|
||||
}
|
||||
|
||||
// helpSuccess is the help command using as many args as possible that would succeed.
|
||||
helpSuccess := "go help"
|
||||
if i > 0 {
|
||||
helpSuccess = " " + strings.Join(args[:i], " ")
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "go help %s: unknown help topic. Run '%s'.\n", strings.Join(args, " "), helpSuccess)
|
||||
os.Exit(2) // failed at 'go help cmd'
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'go help'.\n", arg)
|
||||
os.Exit(2) // failed at 'go help cmd'
|
||||
if len(cmd.Commands) > 0 {
|
||||
PrintUsage(os.Stdout, cmd)
|
||||
} else {
|
||||
tmpl(os.Stdout, helpTemplate, cmd)
|
||||
}
|
||||
// not exit 2: succeeded at 'go help cmd'.
|
||||
return
|
||||
}
|
||||
|
||||
var usageTemplate = `Go is a tool for managing Go source code.
|
||||
var usageTemplate = `{{.Long | trim}}
|
||||
|
||||
Usage:
|
||||
|
||||
go command [arguments]
|
||||
{{.UsageLine}} <command> [arguments]
|
||||
|
||||
The commands are:
|
||||
{{range .}}{{if .Runnable}}
|
||||
{{range .Commands}}{{if or (.Runnable) .Commands}}
|
||||
{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
|
||||
|
||||
Use "go help [command]" for more information about a command.
|
||||
|
||||
Use "go help{{with .LongName}} {{.}}{{end}} <command>" for more information about a command.
|
||||
{{if eq (.UsageLine) "go"}}
|
||||
Additional help topics:
|
||||
{{range .}}{{if not .Runnable}}
|
||||
{{range .Commands}}{{if and (not .Runnable) (not .Commands)}}
|
||||
{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
|
||||
|
||||
Use "go help [topic]" for more information about that topic.
|
||||
|
||||
Use "go help{{with .LongName}} {{.}}{{end}} <topic>" for more information about that topic.
|
||||
{{end}}
|
||||
`
|
||||
|
||||
var helpTemplate = `{{if .Runnable}}usage: go {{.UsageLine}}
|
||||
var helpTemplate = `{{if .Runnable}}usage: {{.UsageLine}}
|
||||
|
||||
{{end}}{{.Long | trim}}
|
||||
`
|
||||
|
||||
var documentationTemplate = `{{range .}}{{if .Short}}{{.Short | capitalize}}
|
||||
|
||||
{{end}}{{if .Runnable}}Usage:
|
||||
{{end}}{{if .Commands}}` + usageTemplate + `{{else}}{{if .Runnable}}Usage:
|
||||
|
||||
go {{.UsageLine}}
|
||||
{{.UsageLine}}
|
||||
|
||||
{{end}}{{.Long | trim}}
|
||||
|
||||
|
||||
{{end}}`
|
||||
{{end}}{{end}}`
|
||||
|
||||
// commentWriter writes a Go comment to the underlying io.Writer,
|
||||
// using line comment form (//).
|
||||
|
@ -171,8 +184,8 @@ func capitalize(s string) string {
|
|||
return string(unicode.ToTitle(r)) + s[n:]
|
||||
}
|
||||
|
||||
func PrintUsage(w io.Writer) {
|
||||
func PrintUsage(w io.Writer, cmd *base.Command) {
|
||||
bw := bufio.NewWriter(w)
|
||||
tmpl(bw, usageTemplate, base.Commands)
|
||||
tmpl(bw, usageTemplate, cmd)
|
||||
bw.Flush()
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ the C or C++ compiler, respectively, to use.
|
|||
|
||||
var HelpPackages = &base.Command{
|
||||
UsageLine: "packages",
|
||||
Short: "package lists",
|
||||
Short: "package lists and patterns",
|
||||
Long: `
|
||||
Many commands apply to a set of packages:
|
||||
|
||||
|
@ -54,9 +54,11 @@ for packages to be built with the go tool:
|
|||
|
||||
- "main" denotes the top-level package in a stand-alone executable.
|
||||
|
||||
- "all" expands to all package directories found in all the GOPATH
|
||||
- "all" expands to all packages found in all the GOPATH
|
||||
trees. For example, 'go list all' lists all the packages on the local
|
||||
system.
|
||||
system. When using modules, "all" expands to all packages in
|
||||
the main module and their dependencies, including dependencies
|
||||
needed by tests of any of those.
|
||||
|
||||
- "std" is like all but expands to just the packages in the standard
|
||||
Go library.
|
||||
|
@ -193,6 +195,7 @@ using the named version control system, and then the path inside
|
|||
that repository. The supported version control systems are:
|
||||
|
||||
Bazaar .bzr
|
||||
Fossil .fossil
|
||||
Git .git
|
||||
Mercurial .hg
|
||||
Subversion .svn
|
||||
|
@ -236,7 +239,7 @@ The meta tag should appear as early in the file as possible.
|
|||
In particular, it should appear before any raw JavaScript or CSS,
|
||||
to avoid confusing the go command's restricted parser.
|
||||
|
||||
The vcs is one of "git", "hg", "svn", etc,
|
||||
The vcs is one of "bzr", "fossil", "git", "hg", "svn".
|
||||
|
||||
The repo-root is the root of the version control system
|
||||
containing a scheme and not containing a .vcs qualifier.
|
||||
|
@ -258,12 +261,22 @@ the go tool will verify that https://example.org/?go-get=1 contains the
|
|||
same meta tag and then git clone https://code.org/r/p/exproj into
|
||||
GOPATH/src/example.org.
|
||||
|
||||
New downloaded packages are written to the first directory listed in the GOPATH
|
||||
environment variable (For more details see: 'go help gopath').
|
||||
When using GOPATH, downloaded packages are written to the first directory
|
||||
listed in the GOPATH environment variable.
|
||||
(See 'go help gopath-get' and 'go help gopath'.)
|
||||
|
||||
The go command attempts to download the version of the
|
||||
package appropriate for the Go release being used.
|
||||
Run 'go help get' for more.
|
||||
When using modules, downloaded packages are stored in the module cache.
|
||||
(See 'go help modules-get' and 'go help goproxy'.)
|
||||
|
||||
When using modules, an additional variant of the go-import meta tag is
|
||||
recognized and is preferred over those listing version control systems.
|
||||
That variant uses "mod" as the vcs in the content value, as in:
|
||||
|
||||
<meta name="go-import" content="example.org mod https://code.org/moduleproxy">
|
||||
|
||||
This tag means to fetch modules with paths beginning with example.org
|
||||
from the module proxy available at the URL https://code.org/moduleproxy.
|
||||
See 'go help goproxy' for details about the proxy protocol.
|
||||
|
||||
Import path checking
|
||||
|
||||
|
@ -286,6 +299,9 @@ Import path checking is disabled for code found within vendor trees.
|
|||
This makes it possible to copy code into alternate locations in vendor trees
|
||||
without needing to update import comments.
|
||||
|
||||
Import path checking is also disabled when using modules.
|
||||
Import path comments are obsoleted by the go.mod file's module statement.
|
||||
|
||||
See https://golang.org/s/go14customimport for details.
|
||||
`,
|
||||
}
|
||||
|
@ -358,6 +374,12 @@ in the list.
|
|||
|
||||
See https://golang.org/doc/code.html for an example.
|
||||
|
||||
GOPATH and Modules
|
||||
|
||||
When using modules, GOPATH is no longer used for resolving imports.
|
||||
However, it is still used to store downloaded source code (in GOPATH/pkg/mod)
|
||||
and compiled commands (in GOPATH/bin).
|
||||
|
||||
Internal Directories
|
||||
|
||||
Code in or below a directory named "internal" is importable only
|
||||
|
@ -461,11 +483,21 @@ General-purpose environment variables:
|
|||
Examples are amd64, 386, arm, ppc64.
|
||||
GOBIN
|
||||
The directory where 'go install' will install a command.
|
||||
GOCACHE
|
||||
The directory where the go command will store cached
|
||||
information for reuse in future builds.
|
||||
GOFLAGS
|
||||
A space-separated list of -flag=value settings to apply
|
||||
to go commands by default, when the given flag is known by
|
||||
the current command. Flags listed on the command-line
|
||||
are applied after this list and therefore override it.
|
||||
GOOS
|
||||
The operating system for which to compile code.
|
||||
Examples are linux, darwin, windows, netbsd.
|
||||
GOPATH
|
||||
For more details see: 'go help gopath'.
|
||||
GOPROXY
|
||||
URL of Go module proxy. See 'go help goproxy'.
|
||||
GORACE
|
||||
Options for the race detector.
|
||||
See https://golang.org/doc/articles/race_detector.html.
|
||||
|
@ -474,9 +506,6 @@ General-purpose environment variables:
|
|||
GOTMPDIR
|
||||
The directory where the go command will write
|
||||
temporary source files, packages, and binaries.
|
||||
GOCACHE
|
||||
The directory where the go command will store
|
||||
cached information for reuse in future builds.
|
||||
|
||||
Environment variables for use with cgo:
|
||||
|
||||
|
@ -523,6 +552,9 @@ Architecture-specific environment variables:
|
|||
GOMIPS
|
||||
For GOARCH=mips{,le}, whether to use floating point instructions.
|
||||
Valid values are hardfloat (default), softfloat.
|
||||
GOMIPS64
|
||||
For GOARCH=mips64{,le}, whether to use floating point instructions.
|
||||
Valid values are hardfloat (default), softfloat.
|
||||
|
||||
Special-purpose environment variables:
|
||||
|
||||
|
@ -542,6 +574,20 @@ Special-purpose environment variables:
|
|||
Defined by Git. A colon-separated list of schemes that are allowed to be used
|
||||
with git fetch/clone. If set, any scheme not explicitly mentioned will be
|
||||
considered insecure by 'go get'.
|
||||
|
||||
Additional information available from 'go env' but not read from the environment:
|
||||
|
||||
GOEXE
|
||||
The executable file name suffix (".exe" on Windows, "" on other systems).
|
||||
GOHOSTARCH
|
||||
The architecture (GOARCH) of the Go toolchain binaries.
|
||||
GOHOSTOS
|
||||
The operating system (GOOS) of the Go toolchain binaries.
|
||||
GOMOD
|
||||
The absolute path to the go.mod of the main module,
|
||||
or the empty string if not using modules.
|
||||
GOTOOLDIR
|
||||
The directory where the go tools (compile, cover, doc, etc...) are installed.
|
||||
`,
|
||||
}
|
||||
|
||||
|
@ -651,6 +697,7 @@ The default location for cache data is a subdirectory named go-build
|
|||
in the standard user cache directory for the current operating system.
|
||||
Setting the GOCACHE environment variable overrides this default,
|
||||
and running 'go env GOCACHE' prints the current cache directory.
|
||||
You can set the variable to 'off' to disable the cache.
|
||||
|
||||
The go command periodically deletes cached data that has not been
|
||||
used recently. Running 'go clean -cache' deletes all cached data.
|
||||
|
|
211
libgo/go/cmd/go/internal/imports/build.go
Normal file
211
libgo/go/cmd/go/internal/imports/build.go
Normal file
|
@ -0,0 +1,211 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Copied from Go distribution src/go/build/build.go, syslist.go
|
||||
|
||||
package imports
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var slashslash = []byte("//")
|
||||
|
||||
// ShouldBuild reports whether it is okay to use this file,
|
||||
// The rule is that in the file's leading run of // comments
|
||||
// and blank lines, which must be followed by a blank line
|
||||
// (to avoid including a Go package clause doc comment),
|
||||
// lines beginning with '// +build' are taken as build directives.
|
||||
//
|
||||
// The file is accepted only if each such line lists something
|
||||
// matching the file. For example:
|
||||
//
|
||||
// // +build windows linux
|
||||
//
|
||||
// marks the file as applicable only on Windows and Linux.
|
||||
//
|
||||
// If tags["*"] is true, then ShouldBuild will consider every
|
||||
// build tag except "ignore" to be both true and false for
|
||||
// the purpose of satisfying build tags, in order to estimate
|
||||
// (conservatively) whether a file could ever possibly be used
|
||||
// in any build.
|
||||
//
|
||||
func ShouldBuild(content []byte, tags map[string]bool) bool {
|
||||
// Pass 1. Identify leading run of // comments and blank lines,
|
||||
// which must be followed by a blank line.
|
||||
end := 0
|
||||
p := content
|
||||
for len(p) > 0 {
|
||||
line := p
|
||||
if i := bytes.IndexByte(line, '\n'); i >= 0 {
|
||||
line, p = line[:i], p[i+1:]
|
||||
} else {
|
||||
p = p[len(p):]
|
||||
}
|
||||
line = bytes.TrimSpace(line)
|
||||
if len(line) == 0 { // Blank line
|
||||
end = len(content) - len(p)
|
||||
continue
|
||||
}
|
||||
if !bytes.HasPrefix(line, slashslash) { // Not comment line
|
||||
break
|
||||
}
|
||||
}
|
||||
content = content[:end]
|
||||
|
||||
// Pass 2. Process each line in the run.
|
||||
p = content
|
||||
allok := true
|
||||
for len(p) > 0 {
|
||||
line := p
|
||||
if i := bytes.IndexByte(line, '\n'); i >= 0 {
|
||||
line, p = line[:i], p[i+1:]
|
||||
} else {
|
||||
p = p[len(p):]
|
||||
}
|
||||
line = bytes.TrimSpace(line)
|
||||
if !bytes.HasPrefix(line, slashslash) {
|
||||
continue
|
||||
}
|
||||
line = bytes.TrimSpace(line[len(slashslash):])
|
||||
if len(line) > 0 && line[0] == '+' {
|
||||
// Looks like a comment +line.
|
||||
f := strings.Fields(string(line))
|
||||
if f[0] == "+build" {
|
||||
ok := false
|
||||
for _, tok := range f[1:] {
|
||||
if matchTags(tok, tags) {
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
allok = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allok
|
||||
}
|
||||
|
||||
// matchTags reports whether the name is one of:
|
||||
//
|
||||
// tag (if tags[tag] is true)
|
||||
// !tag (if tags[tag] is false)
|
||||
// a comma-separated list of any of these
|
||||
//
|
||||
func matchTags(name string, tags map[string]bool) bool {
|
||||
if name == "" {
|
||||
return false
|
||||
}
|
||||
if i := strings.Index(name, ","); i >= 0 {
|
||||
// comma-separated list
|
||||
ok1 := matchTags(name[:i], tags)
|
||||
ok2 := matchTags(name[i+1:], tags)
|
||||
return ok1 && ok2
|
||||
}
|
||||
if strings.HasPrefix(name, "!!") { // bad syntax, reject always
|
||||
return false
|
||||
}
|
||||
if strings.HasPrefix(name, "!") { // negation
|
||||
return len(name) > 1 && matchTag(name[1:], tags, false)
|
||||
}
|
||||
return matchTag(name, tags, true)
|
||||
}
|
||||
|
||||
// matchTag reports whether the tag name is valid and satisfied by tags[name]==want.
|
||||
func matchTag(name string, tags map[string]bool, want bool) bool {
|
||||
// Tags must be letters, digits, underscores or dots.
|
||||
// Unlike in Go identifiers, all digits are fine (e.g., "386").
|
||||
for _, c := range name {
|
||||
if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if tags["*"] && name != "" && name != "ignore" {
|
||||
// Special case for gathering all possible imports:
|
||||
// if we put * in the tags map then all tags
|
||||
// except "ignore" are considered both present and not
|
||||
// (so we return true no matter how 'want' is set).
|
||||
return true
|
||||
}
|
||||
|
||||
have := tags[name]
|
||||
if name == "linux" {
|
||||
have = have || tags["android"]
|
||||
}
|
||||
return have == want
|
||||
}
|
||||
|
||||
// MatchFile returns false if the name contains a $GOOS or $GOARCH
|
||||
// suffix which does not match the current system.
|
||||
// The recognized name formats are:
|
||||
//
|
||||
// name_$(GOOS).*
|
||||
// name_$(GOARCH).*
|
||||
// name_$(GOOS)_$(GOARCH).*
|
||||
// name_$(GOOS)_test.*
|
||||
// name_$(GOARCH)_test.*
|
||||
// name_$(GOOS)_$(GOARCH)_test.*
|
||||
//
|
||||
// An exception: if GOOS=android, then files with GOOS=linux are also matched.
|
||||
//
|
||||
// If tags["*"] is true, then MatchFile will consider all possible
|
||||
// GOOS and GOARCH to be available and will consequently
|
||||
// always return true.
|
||||
func MatchFile(name string, tags map[string]bool) bool {
|
||||
if tags["*"] {
|
||||
return true
|
||||
}
|
||||
if dot := strings.Index(name, "."); dot != -1 {
|
||||
name = name[:dot]
|
||||
}
|
||||
|
||||
// Before Go 1.4, a file called "linux.go" would be equivalent to having a
|
||||
// build tag "linux" in that file. For Go 1.4 and beyond, we require this
|
||||
// auto-tagging to apply only to files with a non-empty prefix, so
|
||||
// "foo_linux.go" is tagged but "linux.go" is not. This allows new operating
|
||||
// systems, such as android, to arrive without breaking existing code with
|
||||
// innocuous source code in "android.go". The easiest fix: cut everything
|
||||
// in the name before the initial _.
|
||||
i := strings.Index(name, "_")
|
||||
if i < 0 {
|
||||
return true
|
||||
}
|
||||
name = name[i:] // ignore everything before first _
|
||||
|
||||
l := strings.Split(name, "_")
|
||||
if n := len(l); n > 0 && l[n-1] == "test" {
|
||||
l = l[:n-1]
|
||||
}
|
||||
n := len(l)
|
||||
if n >= 2 && KnownOS[l[n-2]] && KnownArch[l[n-1]] {
|
||||
return tags[l[n-2]] && tags[l[n-1]]
|
||||
}
|
||||
if n >= 1 && KnownOS[l[n-1]] {
|
||||
return tags[l[n-1]]
|
||||
}
|
||||
if n >= 1 && KnownArch[l[n-1]] {
|
||||
return tags[l[n-1]]
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var KnownOS = make(map[string]bool)
|
||||
var KnownArch = make(map[string]bool)
|
||||
|
||||
func init() {
|
||||
for _, v := range strings.Fields(goosList) {
|
||||
KnownOS[v] = true
|
||||
}
|
||||
for _, v := range strings.Fields(goarchList) {
|
||||
KnownArch[v] = true
|
||||
}
|
||||
}
|
||||
|
||||
const goosList = "android darwin dragonfly freebsd js linux nacl netbsd openbsd plan9 solaris windows zos "
|
||||
const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc riscv riscv64 s390 s390x sparc sparc64 wasm "
|
249
libgo/go/cmd/go/internal/imports/read.go
Normal file
249
libgo/go/cmd/go/internal/imports/read.go
Normal file
|
@ -0,0 +1,249 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Copied from Go distribution src/go/build/read.go.
|
||||
|
||||
package imports
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"io"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type importReader struct {
|
||||
b *bufio.Reader
|
||||
buf []byte
|
||||
peek byte
|
||||
err error
|
||||
eof bool
|
||||
nerr int
|
||||
}
|
||||
|
||||
func isIdent(c byte) bool {
|
||||
return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf
|
||||
}
|
||||
|
||||
var (
|
||||
errSyntax = errors.New("syntax error")
|
||||
errNUL = errors.New("unexpected NUL in input")
|
||||
)
|
||||
|
||||
// syntaxError records a syntax error, but only if an I/O error has not already been recorded.
|
||||
func (r *importReader) syntaxError() {
|
||||
if r.err == nil {
|
||||
r.err = errSyntax
|
||||
}
|
||||
}
|
||||
|
||||
// readByte reads the next byte from the input, saves it in buf, and returns it.
|
||||
// If an error occurs, readByte records the error in r.err and returns 0.
|
||||
func (r *importReader) readByte() byte {
|
||||
c, err := r.b.ReadByte()
|
||||
if err == nil {
|
||||
r.buf = append(r.buf, c)
|
||||
if c == 0 {
|
||||
err = errNUL
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
r.eof = true
|
||||
} else if r.err == nil {
|
||||
r.err = err
|
||||
}
|
||||
c = 0
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// peekByte returns the next byte from the input reader but does not advance beyond it.
|
||||
// If skipSpace is set, peekByte skips leading spaces and comments.
|
||||
func (r *importReader) peekByte(skipSpace bool) byte {
|
||||
if r.err != nil {
|
||||
if r.nerr++; r.nerr > 10000 {
|
||||
panic("go/build: import reader looping")
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Use r.peek as first input byte.
|
||||
// Don't just return r.peek here: it might have been left by peekByte(false)
|
||||
// and this might be peekByte(true).
|
||||
c := r.peek
|
||||
if c == 0 {
|
||||
c = r.readByte()
|
||||
}
|
||||
for r.err == nil && !r.eof {
|
||||
if skipSpace {
|
||||
// For the purposes of this reader, semicolons are never necessary to
|
||||
// understand the input and are treated as spaces.
|
||||
switch c {
|
||||
case ' ', '\f', '\t', '\r', '\n', ';':
|
||||
c = r.readByte()
|
||||
continue
|
||||
|
||||
case '/':
|
||||
c = r.readByte()
|
||||
if c == '/' {
|
||||
for c != '\n' && r.err == nil && !r.eof {
|
||||
c = r.readByte()
|
||||
}
|
||||
} else if c == '*' {
|
||||
var c1 byte
|
||||
for (c != '*' || c1 != '/') && r.err == nil {
|
||||
if r.eof {
|
||||
r.syntaxError()
|
||||
}
|
||||
c, c1 = c1, r.readByte()
|
||||
}
|
||||
} else {
|
||||
r.syntaxError()
|
||||
}
|
||||
c = r.readByte()
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
r.peek = c
|
||||
return r.peek
|
||||
}
|
||||
|
||||
// nextByte is like peekByte but advances beyond the returned byte.
|
||||
func (r *importReader) nextByte(skipSpace bool) byte {
|
||||
c := r.peekByte(skipSpace)
|
||||
r.peek = 0
|
||||
return c
|
||||
}
|
||||
|
||||
// readKeyword reads the given keyword from the input.
|
||||
// If the keyword is not present, readKeyword records a syntax error.
|
||||
func (r *importReader) readKeyword(kw string) {
|
||||
r.peekByte(true)
|
||||
for i := 0; i < len(kw); i++ {
|
||||
if r.nextByte(false) != kw[i] {
|
||||
r.syntaxError()
|
||||
return
|
||||
}
|
||||
}
|
||||
if isIdent(r.peekByte(false)) {
|
||||
r.syntaxError()
|
||||
}
|
||||
}
|
||||
|
||||
// readIdent reads an identifier from the input.
|
||||
// If an identifier is not present, readIdent records a syntax error.
|
||||
func (r *importReader) readIdent() {
|
||||
c := r.peekByte(true)
|
||||
if !isIdent(c) {
|
||||
r.syntaxError()
|
||||
return
|
||||
}
|
||||
for isIdent(r.peekByte(false)) {
|
||||
r.peek = 0
|
||||
}
|
||||
}
|
||||
|
||||
// readString reads a quoted string literal from the input.
|
||||
// If an identifier is not present, readString records a syntax error.
|
||||
func (r *importReader) readString(save *[]string) {
|
||||
switch r.nextByte(true) {
|
||||
case '`':
|
||||
start := len(r.buf) - 1
|
||||
for r.err == nil {
|
||||
if r.nextByte(false) == '`' {
|
||||
if save != nil {
|
||||
*save = append(*save, string(r.buf[start:]))
|
||||
}
|
||||
break
|
||||
}
|
||||
if r.eof {
|
||||
r.syntaxError()
|
||||
}
|
||||
}
|
||||
case '"':
|
||||
start := len(r.buf) - 1
|
||||
for r.err == nil {
|
||||
c := r.nextByte(false)
|
||||
if c == '"' {
|
||||
if save != nil {
|
||||
*save = append(*save, string(r.buf[start:]))
|
||||
}
|
||||
break
|
||||
}
|
||||
if r.eof || c == '\n' {
|
||||
r.syntaxError()
|
||||
}
|
||||
if c == '\\' {
|
||||
r.nextByte(false)
|
||||
}
|
||||
}
|
||||
default:
|
||||
r.syntaxError()
|
||||
}
|
||||
}
|
||||
|
||||
// readImport reads an import clause - optional identifier followed by quoted string -
|
||||
// from the input.
|
||||
func (r *importReader) readImport(imports *[]string) {
|
||||
c := r.peekByte(true)
|
||||
if c == '.' {
|
||||
r.peek = 0
|
||||
} else if isIdent(c) {
|
||||
r.readIdent()
|
||||
}
|
||||
r.readString(imports)
|
||||
}
|
||||
|
||||
// ReadComments is like ioutil.ReadAll, except that it only reads the leading
|
||||
// block of comments in the file.
|
||||
func ReadComments(f io.Reader) ([]byte, error) {
|
||||
r := &importReader{b: bufio.NewReader(f)}
|
||||
r.peekByte(true)
|
||||
if r.err == nil && !r.eof {
|
||||
// Didn't reach EOF, so must have found a non-space byte. Remove it.
|
||||
r.buf = r.buf[:len(r.buf)-1]
|
||||
}
|
||||
return r.buf, r.err
|
||||
}
|
||||
|
||||
// ReadImports is like ioutil.ReadAll, except that it expects a Go file as input
|
||||
// and stops reading the input once the imports have completed.
|
||||
func ReadImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, error) {
|
||||
r := &importReader{b: bufio.NewReader(f)}
|
||||
|
||||
r.readKeyword("package")
|
||||
r.readIdent()
|
||||
for r.peekByte(true) == 'i' {
|
||||
r.readKeyword("import")
|
||||
if r.peekByte(true) == '(' {
|
||||
r.nextByte(false)
|
||||
for r.peekByte(true) != ')' && r.err == nil {
|
||||
r.readImport(imports)
|
||||
}
|
||||
r.nextByte(false)
|
||||
} else {
|
||||
r.readImport(imports)
|
||||
}
|
||||
}
|
||||
|
||||
// If we stopped successfully before EOF, we read a byte that told us we were done.
|
||||
// Return all but that last byte, which would cause a syntax error if we let it through.
|
||||
if r.err == nil && !r.eof {
|
||||
return r.buf[:len(r.buf)-1], nil
|
||||
}
|
||||
|
||||
// If we stopped for a syntax error, consume the whole file so that
|
||||
// we are sure we don't change the errors that go/parser returns.
|
||||
if r.err == errSyntax && !reportSyntaxError {
|
||||
r.err = nil
|
||||
for r.err == nil && !r.eof {
|
||||
r.readByte()
|
||||
}
|
||||
}
|
||||
|
||||
return r.buf, r.err
|
||||
}
|
228
libgo/go/cmd/go/internal/imports/read_test.go
Normal file
228
libgo/go/cmd/go/internal/imports/read_test.go
Normal file
|
@ -0,0 +1,228 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Copied from Go distribution src/go/build/read.go.
|
||||
|
||||
package imports
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const quote = "`"
|
||||
|
||||
type readTest struct {
|
||||
// Test input contains ℙ where readImports should stop.
|
||||
in string
|
||||
err string
|
||||
}
|
||||
|
||||
var readImportsTests = []readTest{
|
||||
{
|
||||
`package p`,
|
||||
"",
|
||||
},
|
||||
{
|
||||
`package p; import "x"`,
|
||||
"",
|
||||
},
|
||||
{
|
||||
`package p; import . "x"`,
|
||||
"",
|
||||
},
|
||||
{
|
||||
`package p; import "x";ℙvar x = 1`,
|
||||
"",
|
||||
},
|
||||
{
|
||||
`package p
|
||||
|
||||
// comment
|
||||
|
||||
import "x"
|
||||
import _ "x"
|
||||
import a "x"
|
||||
|
||||
/* comment */
|
||||
|
||||
import (
|
||||
"x" /* comment */
|
||||
_ "x"
|
||||
a "x" // comment
|
||||
` + quote + `x` + quote + `
|
||||
_ /*comment*/ ` + quote + `x` + quote + `
|
||||
a ` + quote + `x` + quote + `
|
||||
)
|
||||
import (
|
||||
)
|
||||
import ()
|
||||
import()import()import()
|
||||
import();import();import()
|
||||
|
||||
ℙvar x = 1
|
||||
`,
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
||||
var readCommentsTests = []readTest{
|
||||
{
|
||||
`ℙpackage p`,
|
||||
"",
|
||||
},
|
||||
{
|
||||
`ℙpackage p; import "x"`,
|
||||
"",
|
||||
},
|
||||
{
|
||||
`ℙpackage p; import . "x"`,
|
||||
"",
|
||||
},
|
||||
{
|
||||
`// foo
|
||||
|
||||
/* bar */
|
||||
|
||||
/* quux */ // baz
|
||||
|
||||
/*/ zot */
|
||||
|
||||
// asdf
|
||||
ℙHello, world`,
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
||||
func testRead(t *testing.T, tests []readTest, read func(io.Reader) ([]byte, error)) {
|
||||
for i, tt := range tests {
|
||||
var in, testOut string
|
||||
j := strings.Index(tt.in, "ℙ")
|
||||
if j < 0 {
|
||||
in = tt.in
|
||||
testOut = tt.in
|
||||
} else {
|
||||
in = tt.in[:j] + tt.in[j+len("ℙ"):]
|
||||
testOut = tt.in[:j]
|
||||
}
|
||||
r := strings.NewReader(in)
|
||||
buf, err := read(r)
|
||||
if err != nil {
|
||||
if tt.err == "" {
|
||||
t.Errorf("#%d: err=%q, expected success (%q)", i, err, string(buf))
|
||||
continue
|
||||
}
|
||||
if !strings.Contains(err.Error(), tt.err) {
|
||||
t.Errorf("#%d: err=%q, expected %q", i, err, tt.err)
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err == nil && tt.err != "" {
|
||||
t.Errorf("#%d: success, expected %q", i, tt.err)
|
||||
continue
|
||||
}
|
||||
|
||||
out := string(buf)
|
||||
if out != testOut {
|
||||
t.Errorf("#%d: wrong output:\nhave %q\nwant %q\n", i, out, testOut)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadImports(t *testing.T) {
|
||||
testRead(t, readImportsTests, func(r io.Reader) ([]byte, error) { return ReadImports(r, true, nil) })
|
||||
}
|
||||
|
||||
func TestReadComments(t *testing.T) {
|
||||
testRead(t, readCommentsTests, ReadComments)
|
||||
}
|
||||
|
||||
var readFailuresTests = []readTest{
|
||||
{
|
||||
`package`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
"package p\n\x00\nimport `math`\n",
|
||||
"unexpected NUL in input",
|
||||
},
|
||||
{
|
||||
`package p; import`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import "`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
"package p; import ` \n\n",
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import "x`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import _`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import _ "`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import _ "x`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import .`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import . "`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import . "x`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import (`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import ("`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import ("x`,
|
||||
"syntax error",
|
||||
},
|
||||
{
|
||||
`package p; import ("x"`,
|
||||
"syntax error",
|
||||
},
|
||||
}
|
||||
|
||||
func TestReadFailures(t *testing.T) {
|
||||
// Errors should be reported (true arg to readImports).
|
||||
testRead(t, readFailuresTests, func(r io.Reader) ([]byte, error) { return ReadImports(r, true, nil) })
|
||||
}
|
||||
|
||||
func TestReadFailuresIgnored(t *testing.T) {
|
||||
// Syntax errors should not be reported (false arg to readImports).
|
||||
// Instead, entire file should be the output and no error.
|
||||
// Convert tests not to return syntax errors.
|
||||
tests := make([]readTest, len(readFailuresTests))
|
||||
copy(tests, readFailuresTests)
|
||||
for i := range tests {
|
||||
tt := &tests[i]
|
||||
if !strings.Contains(tt.err, "NUL") {
|
||||
tt.err = ""
|
||||
}
|
||||
}
|
||||
testRead(t, tests, func(r io.Reader) ([]byte, error) { return ReadImports(r, false, nil) })
|
||||
}
|
96
libgo/go/cmd/go/internal/imports/scan.go
Normal file
96
libgo/go/cmd/go/internal/imports/scan.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package imports
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ScanDir(dir string, tags map[string]bool) ([]string, []string, error) {
|
||||
infos, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var files []string
|
||||
for _, info := range infos {
|
||||
name := info.Name()
|
||||
if info.Mode().IsRegular() && !strings.HasPrefix(name, "_") && strings.HasSuffix(name, ".go") && MatchFile(name, tags) {
|
||||
files = append(files, filepath.Join(dir, name))
|
||||
}
|
||||
}
|
||||
return scanFiles(files, tags, false)
|
||||
}
|
||||
|
||||
func ScanFiles(files []string, tags map[string]bool) ([]string, []string, error) {
|
||||
return scanFiles(files, tags, true)
|
||||
}
|
||||
|
||||
func scanFiles(files []string, tags map[string]bool, explicitFiles bool) ([]string, []string, error) {
|
||||
imports := make(map[string]bool)
|
||||
testImports := make(map[string]bool)
|
||||
numFiles := 0
|
||||
Files:
|
||||
for _, name := range files {
|
||||
r, err := os.Open(name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var list []string
|
||||
data, err := ReadImports(r, false, &list)
|
||||
r.Close()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("reading %s: %v", name, err)
|
||||
}
|
||||
|
||||
// import "C" is implicit requirement of cgo tag.
|
||||
// When listing files on the command line (explicitFiles=true)
|
||||
// we do not apply build tag filtering but we still do apply
|
||||
// cgo filtering, so no explicitFiles check here.
|
||||
// Why? Because we always have, and it's not worth breaking
|
||||
// that behavior now.
|
||||
for _, path := range list {
|
||||
if path == `"C"` && !tags["cgo"] && !tags["*"] {
|
||||
continue Files
|
||||
}
|
||||
}
|
||||
|
||||
if !explicitFiles && !ShouldBuild(data, tags) {
|
||||
continue
|
||||
}
|
||||
numFiles++
|
||||
m := imports
|
||||
if strings.HasSuffix(name, "_test.go") {
|
||||
m = testImports
|
||||
}
|
||||
for _, p := range list {
|
||||
q, err := strconv.Unquote(p)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
m[q] = true
|
||||
}
|
||||
}
|
||||
if numFiles == 0 {
|
||||
return nil, nil, ErrNoGo
|
||||
}
|
||||
return keys(imports), keys(testImports), nil
|
||||
}
|
||||
|
||||
var ErrNoGo = fmt.Errorf("no Go source files")
|
||||
|
||||
func keys(m map[string]bool) []string {
|
||||
var list []string
|
||||
for k := range m {
|
||||
list = append(list, k)
|
||||
}
|
||||
sort.Strings(list)
|
||||
return list
|
||||
}
|
67
libgo/go/cmd/go/internal/imports/scan_test.go
Normal file
67
libgo/go/cmd/go/internal/imports/scan_test.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package imports
|
||||
|
||||
import (
|
||||
"internal/testenv"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestScan(t *testing.T) {
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
imports, testImports, err := ScanDir(filepath.Join(runtime.GOROOT(), "src/encoding/json"), Tags())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
foundBase64 := false
|
||||
for _, p := range imports {
|
||||
if p == "encoding/base64" {
|
||||
foundBase64 = true
|
||||
}
|
||||
if p == "encoding/binary" {
|
||||
// A dependency but not an import
|
||||
t.Errorf("json reported as importing encoding/binary but does not")
|
||||
}
|
||||
if p == "net/http" {
|
||||
// A test import but not an import
|
||||
t.Errorf("json reported as importing encoding/binary but does not")
|
||||
}
|
||||
}
|
||||
if !foundBase64 {
|
||||
t.Errorf("json missing import encoding/base64 (%q)", imports)
|
||||
}
|
||||
|
||||
foundHTTP := false
|
||||
for _, p := range testImports {
|
||||
if p == "net/http" {
|
||||
foundHTTP = true
|
||||
}
|
||||
if p == "unicode/utf16" {
|
||||
// A package import but not a test import
|
||||
t.Errorf("json reported as test-importing unicode/utf16 but does not")
|
||||
}
|
||||
}
|
||||
if !foundHTTP {
|
||||
t.Errorf("json missing test import net/http (%q)", testImports)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanStar(t *testing.T) {
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
imports, _, err := ScanDir("testdata/import1", map[string]bool{"*": true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
want := []string{"import1", "import2", "import3", "import4"}
|
||||
if !reflect.DeepEqual(imports, want) {
|
||||
t.Errorf("ScanDir testdata/import1:\nhave %v\nwant %v", imports, want)
|
||||
}
|
||||
}
|
34
libgo/go/cmd/go/internal/imports/tags.go
Normal file
34
libgo/go/cmd/go/internal/imports/tags.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package imports
|
||||
|
||||
import "cmd/go/internal/cfg"
|
||||
|
||||
var tags map[string]bool
|
||||
|
||||
func Tags() map[string]bool {
|
||||
if tags == nil {
|
||||
tags = loadTags()
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
func loadTags() map[string]bool {
|
||||
tags := map[string]bool{
|
||||
cfg.BuildContext.GOOS: true,
|
||||
cfg.BuildContext.GOARCH: true,
|
||||
cfg.BuildContext.Compiler: true,
|
||||
}
|
||||
if cfg.BuildContext.CgoEnabled {
|
||||
tags["cgo"] = true
|
||||
}
|
||||
for _, tag := range cfg.BuildContext.BuildTags {
|
||||
tags[tag] = true
|
||||
}
|
||||
for _, tag := range cfg.BuildContext.ReleaseTags {
|
||||
tags[tag] = true
|
||||
}
|
||||
return tags
|
||||
}
|
3
libgo/go/cmd/go/internal/imports/testdata/import1/x.go
vendored
Normal file
3
libgo/go/cmd/go/internal/imports/testdata/import1/x.go
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
package x
|
||||
|
||||
import "import1"
|
9
libgo/go/cmd/go/internal/imports/testdata/import1/x1.go
vendored
Normal file
9
libgo/go/cmd/go/internal/imports/testdata/import1/x1.go
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
// +build blahblh
|
||||
// +build linux
|
||||
// +build !linux
|
||||
// +build windows
|
||||
// +build darwin
|
||||
|
||||
package x
|
||||
|
||||
import "import4"
|
3
libgo/go/cmd/go/internal/imports/testdata/import1/x_darwin.go
vendored
Normal file
3
libgo/go/cmd/go/internal/imports/testdata/import1/x_darwin.go
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
package xxxx
|
||||
|
||||
import "import3"
|
3
libgo/go/cmd/go/internal/imports/testdata/import1/x_windows.go
vendored
Normal file
3
libgo/go/cmd/go/internal/imports/testdata/import1/x_windows.go
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
package x
|
||||
|
||||
import "import2"
|
|
@ -7,23 +7,33 @@ package list
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cache"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/load"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/str"
|
||||
"cmd/go/internal/work"
|
||||
)
|
||||
|
||||
var CmdList = &base.Command{
|
||||
UsageLine: "list [-e] [-f format] [-json] [build flags] [packages]",
|
||||
Short: "list packages",
|
||||
// Note: -f -json -m are listed explicitly because they are the most common list flags.
|
||||
// Do not send CLs removing them because they're covered by [list flags].
|
||||
UsageLine: "go list [-f format] [-json] [-m] [list flags] [build flags] [packages]",
|
||||
Short: "list packages or modules",
|
||||
Long: `
|
||||
List lists the packages named by the import paths, one per line.
|
||||
List lists the named packages, one per line.
|
||||
The most commonly-used flags are -f and -json, which control the form
|
||||
of the output printed for each package. Other list flags, documented below,
|
||||
control more specific details.
|
||||
|
||||
The default output shows the package import path:
|
||||
|
||||
|
@ -33,40 +43,46 @@ The default output shows the package import path:
|
|||
golang.org/x/net/html
|
||||
|
||||
The -f flag specifies an alternate format for the list, using the
|
||||
syntax of package template. The default output is equivalent to -f
|
||||
'{{.ImportPath}}'. The struct being passed to the template is:
|
||||
syntax of package template. The default output is equivalent
|
||||
to -f '{{.ImportPath}}'. The struct being passed to the template is:
|
||||
|
||||
type Package struct {
|
||||
Dir string // directory containing package sources
|
||||
ImportPath string // import path of package in dir
|
||||
ImportComment string // path in import comment on package statement
|
||||
Name string // package name
|
||||
Doc string // package documentation string
|
||||
Target string // install path
|
||||
Shlib string // the shared library that contains this package (only set when -linkshared)
|
||||
Goroot bool // is this package in the Go root?
|
||||
Standard bool // is this package part of the standard Go library?
|
||||
Stale bool // would 'go install' do anything for this package?
|
||||
StaleReason string // explanation for Stale==true
|
||||
Root string // Go root or Go path dir containing this package
|
||||
ConflictDir string // this directory shadows Dir in $GOPATH
|
||||
BinaryOnly bool // binary-only package: cannot be recompiled from sources
|
||||
Dir string // directory containing package sources
|
||||
ImportPath string // import path of package in dir
|
||||
ImportComment string // path in import comment on package statement
|
||||
Name string // package name
|
||||
Doc string // package documentation string
|
||||
Target string // install path
|
||||
Shlib string // the shared library that contains this package (only set when -linkshared)
|
||||
Goroot bool // is this package in the Go root?
|
||||
Standard bool // is this package part of the standard Go library?
|
||||
Stale bool // would 'go install' do anything for this package?
|
||||
StaleReason string // explanation for Stale==true
|
||||
Root string // Go root or Go path dir containing this package
|
||||
ConflictDir string // this directory shadows Dir in $GOPATH
|
||||
BinaryOnly bool // binary-only package: cannot be recompiled from sources
|
||||
ForTest string // package is only for use in named test
|
||||
Export string // file containing export data (when using -export)
|
||||
Module *Module // info about package's containing module, if any (can be nil)
|
||||
Match []string // command-line patterns matching this package
|
||||
DepOnly bool // package is only a dependency, not explicitly listed
|
||||
|
||||
// Source files
|
||||
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
|
||||
CgoFiles []string // .go sources files that import "C"
|
||||
IgnoredGoFiles []string // .go sources ignored due to build constraints
|
||||
CFiles []string // .c source files
|
||||
CXXFiles []string // .cc, .cxx and .cpp source files
|
||||
MFiles []string // .m source files
|
||||
HFiles []string // .h, .hh, .hpp and .hxx source files
|
||||
FFiles []string // .f, .F, .for and .f90 Fortran source files
|
||||
SFiles []string // .s source files
|
||||
SwigFiles []string // .swig files
|
||||
SwigCXXFiles []string // .swigcxx files
|
||||
SysoFiles []string // .syso object files to add to archive
|
||||
TestGoFiles []string // _test.go files in package
|
||||
XTestGoFiles []string // _test.go files outside package
|
||||
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
|
||||
CgoFiles []string // .go source files that import "C"
|
||||
CompiledGoFiles []string // .go files presented to compiler (when using -compiled)
|
||||
IgnoredGoFiles []string // .go source files ignored due to build constraints
|
||||
CFiles []string // .c source files
|
||||
CXXFiles []string // .cc, .cxx and .cpp source files
|
||||
MFiles []string // .m source files
|
||||
HFiles []string // .h, .hh, .hpp and .hxx source files
|
||||
FFiles []string // .f, .F, .for and .f90 Fortran source files
|
||||
SFiles []string // .s source files
|
||||
SwigFiles []string // .swig files
|
||||
SwigCXXFiles []string // .swigcxx files
|
||||
SysoFiles []string // .syso object files to add to archive
|
||||
TestGoFiles []string // _test.go files in package
|
||||
XTestGoFiles []string // _test.go files outside package
|
||||
|
||||
// Cgo directives
|
||||
CgoCFLAGS []string // cgo: flags for C compiler
|
||||
|
@ -77,10 +93,11 @@ syntax of package template. The default output is equivalent to -f
|
|||
CgoPkgConfig []string // cgo: pkg-config names
|
||||
|
||||
// Dependency information
|
||||
Imports []string // import paths used by this package
|
||||
Deps []string // all (recursively) imported dependencies
|
||||
TestImports []string // imports from TestGoFiles
|
||||
XTestImports []string // imports from XTestGoFiles
|
||||
Imports []string // import paths used by this package
|
||||
ImportMap map[string]string // map from source import to ImportPath (identity entries omitted)
|
||||
Deps []string // all (recursively) imported dependencies
|
||||
TestImports []string // imports from TestGoFiles
|
||||
XTestImports []string // imports from XTestGoFiles
|
||||
|
||||
// Error information
|
||||
Incomplete bool // this package or a dependency has an error
|
||||
|
@ -92,7 +109,7 @@ Packages stored in vendor directories report an ImportPath that includes the
|
|||
path to the vendor directory (for example, "d/vendor/p" instead of "p"),
|
||||
so that the ImportPath uniquely identifies a given copy of a package.
|
||||
The Imports, Deps, TestImports, and XTestImports lists also contain these
|
||||
expanded imports paths. See golang.org/s/go15vendor for more about vendoring.
|
||||
expanded import paths. See golang.org/s/go15vendor for more about vendoring.
|
||||
|
||||
The error information, if any, is
|
||||
|
||||
|
@ -102,22 +119,25 @@ The error information, if any, is
|
|||
Err string // the error itself
|
||||
}
|
||||
|
||||
The module information is a Module struct, defined in the discussion
|
||||
of list -m below.
|
||||
|
||||
The template function "join" calls strings.Join.
|
||||
|
||||
The template function "context" returns the build context, defined as:
|
||||
|
||||
type Context struct {
|
||||
GOARCH string // target architecture
|
||||
GOOS string // target operating system
|
||||
GOROOT string // Go root
|
||||
GOPATH string // Go path
|
||||
CgoEnabled bool // whether cgo can be used
|
||||
UseAllFiles bool // use files regardless of +build lines, file names
|
||||
Compiler string // compiler to assume when computing target paths
|
||||
BuildTags []string // build constraints to match in +build lines
|
||||
ReleaseTags []string // releases the current release is compatible with
|
||||
InstallSuffix string // suffix to use in the name of the install dir
|
||||
}
|
||||
type Context struct {
|
||||
GOARCH string // target architecture
|
||||
GOOS string // target operating system
|
||||
GOROOT string // Go root
|
||||
GOPATH string // Go path
|
||||
CgoEnabled bool // whether cgo can be used
|
||||
UseAllFiles bool // use files regardless of +build lines, file names
|
||||
Compiler string // compiler to assume when computing target paths
|
||||
BuildTags []string // build constraints to match in +build lines
|
||||
ReleaseTags []string // releases the current release is compatible with
|
||||
InstallSuffix string // suffix to use in the name of the install dir
|
||||
}
|
||||
|
||||
For more information about the meaning of these fields see the documentation
|
||||
for the go/build package's Context type.
|
||||
|
@ -125,6 +145,18 @@ for the go/build package's Context type.
|
|||
The -json flag causes the package data to be printed in JSON format
|
||||
instead of using the template format.
|
||||
|
||||
The -compiled flag causes list to set CompiledGoFiles to the Go source
|
||||
files presented to the compiler. Typically this means that it repeats
|
||||
the files listed in GoFiles and then also adds the Go code generated
|
||||
by processing CgoFiles and SwigFiles. The Imports list contains the
|
||||
union of all imports from both GoFiles and CompiledGoFiles.
|
||||
|
||||
The -deps flag causes list to iterate over not just the named packages
|
||||
but also all their dependencies. It visits them in a depth-first post-order
|
||||
traversal, so that a package is listed only after all its dependencies.
|
||||
Packages not explicitly listed on the command line will have the DepOnly
|
||||
field set to true.
|
||||
|
||||
The -e flag changes the handling of erroneous packages, those that
|
||||
cannot be found or are malformed. By default, the list command
|
||||
prints an error to standard error for each erroneous package and
|
||||
|
@ -135,9 +167,120 @@ printing. Erroneous packages will have a non-empty ImportPath and
|
|||
a non-nil Error field; other information may or may not be missing
|
||||
(zeroed).
|
||||
|
||||
The -export flag causes list to set the Export field to the name of a
|
||||
file containing up-to-date export information for the given package.
|
||||
|
||||
The -find flag causes list to identify the named packages but not
|
||||
resolve their dependencies: the Imports and Deps lists will be empty.
|
||||
|
||||
The -test flag causes list to report not only the named packages
|
||||
but also their test binaries (for packages with tests), to convey to
|
||||
source code analysis tools exactly how test binaries are constructed.
|
||||
The reported import path for a test binary is the import path of
|
||||
the package followed by a ".test" suffix, as in "math/rand.test".
|
||||
When building a test, it is sometimes necessary to rebuild certain
|
||||
dependencies specially for that test (most commonly the tested
|
||||
package itself). The reported import path of a package recompiled
|
||||
for a particular test binary is followed by a space and the name of
|
||||
the test binary in brackets, as in "math/rand [math/rand.test]"
|
||||
or "regexp [sort.test]". The ForTest field is also set to the name
|
||||
of the package being tested ("math/rand" or "sort" in the previous
|
||||
examples).
|
||||
|
||||
The Dir, Target, Shlib, Root, ConflictDir, and Export file paths
|
||||
are all absolute paths.
|
||||
|
||||
By default, the lists GoFiles, CgoFiles, and so on hold names of files in Dir
|
||||
(that is, paths relative to Dir, not absolute paths).
|
||||
The generated files added when using the -compiled and -test flags
|
||||
are absolute paths referring to cached copies of generated Go source files.
|
||||
Although they are Go source files, the paths may not end in ".go".
|
||||
|
||||
The -m flag causes list to list modules instead of packages.
|
||||
|
||||
When listing modules, the -f flag still specifies a format template
|
||||
applied to a Go struct, but now a Module struct:
|
||||
|
||||
type Module struct {
|
||||
Path string // module path
|
||||
Version string // module version
|
||||
Versions []string // available module versions (with -versions)
|
||||
Replace *Module // replaced by this module
|
||||
Time *time.Time // time version was created
|
||||
Update *Module // available update, if any (with -u)
|
||||
Main bool // is this the main module?
|
||||
Indirect bool // is this module only an indirect dependency of main module?
|
||||
Dir string // directory holding files for this module, if any
|
||||
GoMod string // path to go.mod file for this module, if any
|
||||
Error *ModuleError // error loading module
|
||||
}
|
||||
|
||||
type ModuleError struct {
|
||||
Err string // the error itself
|
||||
}
|
||||
|
||||
The default output is to print the module path and then
|
||||
information about the version and replacement if any.
|
||||
For example, 'go list -m all' might print:
|
||||
|
||||
my/main/module
|
||||
golang.org/x/text v0.3.0 => /tmp/text
|
||||
rsc.io/pdf v0.1.1
|
||||
|
||||
The Module struct has a String method that formats this
|
||||
line of output, so that the default format is equivalent
|
||||
to -f '{{.String}}'.
|
||||
|
||||
Note that when a module has been replaced, its Replace field
|
||||
describes the replacement module, and its Dir field is set to
|
||||
the replacement's source code, if present. (That is, if Replace
|
||||
is non-nil, then Dir is set to Replace.Dir, with no access to
|
||||
the replaced source code.)
|
||||
|
||||
The -u flag adds information about available upgrades.
|
||||
When the latest version of a given module is newer than
|
||||
the current one, list -u sets the Module's Update field
|
||||
to information about the newer module.
|
||||
The Module's String method indicates an available upgrade by
|
||||
formatting the newer version in brackets after the current version.
|
||||
For example, 'go list -m -u all' might print:
|
||||
|
||||
my/main/module
|
||||
golang.org/x/text v0.3.0 [v0.4.0] => /tmp/text
|
||||
rsc.io/pdf v0.1.1 [v0.1.2]
|
||||
|
||||
(For tools, 'go list -m -u -json all' may be more convenient to parse.)
|
||||
|
||||
The -versions flag causes list to set the Module's Versions field
|
||||
to a list of all known versions of that module, ordered according
|
||||
to semantic versioning, earliest to latest. The flag also changes
|
||||
the default output format to display the module path followed by the
|
||||
space-separated version list.
|
||||
|
||||
The arguments to list -m are interpreted as a list of modules, not packages.
|
||||
The main module is the module containing the current directory.
|
||||
The active modules are the main module and its dependencies.
|
||||
With no arguments, list -m shows the main module.
|
||||
With arguments, list -m shows the modules specified by the arguments.
|
||||
Any of the active modules can be specified by its module path.
|
||||
The special pattern "all" specifies all the active modules, first the main
|
||||
module and then dependencies sorted by module path.
|
||||
A pattern containing "..." specifies the active modules whose
|
||||
module paths match the pattern.
|
||||
A query of the form path@version specifies the result of that query,
|
||||
which is not limited to active modules.
|
||||
See 'go help modules' for more about module queries.
|
||||
|
||||
The template function "module" takes a single string argument
|
||||
that must be a module path or query and returns the specified
|
||||
module as a Module struct. If an error occurs, the result will
|
||||
be a Module struct with a non-nil Error field.
|
||||
|
||||
For more about build flags, see 'go help build'.
|
||||
|
||||
For more about specifying packages, see 'go help packages'.
|
||||
|
||||
For more about modules, see 'go help modules'.
|
||||
`,
|
||||
}
|
||||
|
||||
|
@ -146,20 +289,43 @@ func init() {
|
|||
work.AddBuildFlags(CmdList)
|
||||
}
|
||||
|
||||
var listE = CmdList.Flag.Bool("e", false, "")
|
||||
var listFmt = CmdList.Flag.String("f", "{{.ImportPath}}", "")
|
||||
var listJson = CmdList.Flag.Bool("json", false, "")
|
||||
var (
|
||||
listCompiled = CmdList.Flag.Bool("compiled", false, "")
|
||||
listDeps = CmdList.Flag.Bool("deps", false, "")
|
||||
listE = CmdList.Flag.Bool("e", false, "")
|
||||
listExport = CmdList.Flag.Bool("export", false, "")
|
||||
listFmt = CmdList.Flag.String("f", "", "")
|
||||
listFind = CmdList.Flag.Bool("find", false, "")
|
||||
listJson = CmdList.Flag.Bool("json", false, "")
|
||||
listM = CmdList.Flag.Bool("m", false, "")
|
||||
listU = CmdList.Flag.Bool("u", false, "")
|
||||
listTest = CmdList.Flag.Bool("test", false, "")
|
||||
listVersions = CmdList.Flag.Bool("versions", false, "")
|
||||
)
|
||||
|
||||
var nl = []byte{'\n'}
|
||||
|
||||
func runList(cmd *base.Command, args []string) {
|
||||
modload.LoadTests = *listTest
|
||||
work.BuildInit()
|
||||
out := newTrackingWriter(os.Stdout)
|
||||
defer out.w.Flush()
|
||||
|
||||
var do func(*load.PackagePublic)
|
||||
if *listFmt == "" {
|
||||
if *listM {
|
||||
*listFmt = "{{.String}}"
|
||||
if *listVersions {
|
||||
*listFmt = `{{.Path}}{{range .Versions}} {{.}}{{end}}`
|
||||
}
|
||||
} else {
|
||||
*listFmt = "{{.ImportPath}}"
|
||||
}
|
||||
}
|
||||
|
||||
var do func(interface{})
|
||||
if *listJson {
|
||||
do = func(p *load.PackagePublic) {
|
||||
b, err := json.MarshalIndent(p, "", "\t")
|
||||
do = func(x interface{}) {
|
||||
b, err := json.MarshalIndent(x, "", "\t")
|
||||
if err != nil {
|
||||
out.Flush()
|
||||
base.Fatalf("%s", err)
|
||||
|
@ -178,13 +344,14 @@ func runList(cmd *base.Command, args []string) {
|
|||
fm := template.FuncMap{
|
||||
"join": strings.Join,
|
||||
"context": context,
|
||||
"module": modload.ModuleInfo,
|
||||
}
|
||||
tmpl, err := template.New("main").Funcs(fm).Parse(*listFmt)
|
||||
if err != nil {
|
||||
base.Fatalf("%s", err)
|
||||
}
|
||||
do = func(p *load.PackagePublic) {
|
||||
if err := tmpl.Execute(out, p); err != nil {
|
||||
do = func(x interface{}) {
|
||||
if err := tmpl.Execute(out, x); err != nil {
|
||||
out.Flush()
|
||||
base.Fatalf("%s", err)
|
||||
}
|
||||
|
@ -194,6 +361,62 @@ func runList(cmd *base.Command, args []string) {
|
|||
}
|
||||
}
|
||||
|
||||
if *listM {
|
||||
// Module mode.
|
||||
if *listCompiled {
|
||||
base.Fatalf("go list -compiled cannot be used with -m")
|
||||
}
|
||||
if *listDeps {
|
||||
// TODO(rsc): Could make this mean something with -m.
|
||||
base.Fatalf("go list -deps cannot be used with -m")
|
||||
}
|
||||
if *listExport {
|
||||
base.Fatalf("go list -export cannot be used with -m")
|
||||
}
|
||||
if *listFind {
|
||||
base.Fatalf("go list -find cannot be used with -m")
|
||||
}
|
||||
if *listTest {
|
||||
base.Fatalf("go list -test cannot be used with -m")
|
||||
}
|
||||
|
||||
if modload.Init(); !modload.Enabled() {
|
||||
base.Fatalf("go list -m: not using modules")
|
||||
}
|
||||
modload.LoadBuildList()
|
||||
|
||||
mods := modload.ListModules(args, *listU, *listVersions)
|
||||
if !*listE {
|
||||
for _, m := range mods {
|
||||
if m.Error != nil {
|
||||
base.Errorf("go list -m %s: %v", m.Path, m.Error.Err)
|
||||
}
|
||||
}
|
||||
base.ExitIfErrors()
|
||||
}
|
||||
for _, m := range mods {
|
||||
do(m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Package mode (not -m).
|
||||
if *listU {
|
||||
base.Fatalf("go list -u can only be used with -m")
|
||||
}
|
||||
if *listVersions {
|
||||
base.Fatalf("go list -versions can only be used with -m")
|
||||
}
|
||||
|
||||
// These pairings make no sense.
|
||||
if *listFind && *listDeps {
|
||||
base.Fatalf("go list -deps cannot be used with -find")
|
||||
}
|
||||
if *listFind && *listTest {
|
||||
base.Fatalf("go list -test cannot be used with -find")
|
||||
}
|
||||
|
||||
load.IgnoreImports = *listFind
|
||||
var pkgs []*load.Package
|
||||
if *listE {
|
||||
pkgs = load.PackagesAndErrors(args)
|
||||
|
@ -201,27 +424,178 @@ func runList(cmd *base.Command, args []string) {
|
|||
pkgs = load.Packages(args)
|
||||
}
|
||||
|
||||
// Estimate whether staleness information is needed,
|
||||
// since it's a little bit of work to compute.
|
||||
if cache.Default() == nil {
|
||||
// These flags return file names pointing into the build cache,
|
||||
// so the build cache must exist.
|
||||
if *listCompiled {
|
||||
base.Fatalf("go list -compiled requires build cache")
|
||||
}
|
||||
if *listExport {
|
||||
base.Fatalf("go list -export requires build cache")
|
||||
}
|
||||
if *listTest {
|
||||
base.Fatalf("go list -test requires build cache")
|
||||
}
|
||||
}
|
||||
|
||||
if *listTest {
|
||||
c := cache.Default()
|
||||
// Add test binaries to packages to be listed.
|
||||
for _, p := range pkgs {
|
||||
if p.Error != nil {
|
||||
continue
|
||||
}
|
||||
if len(p.TestGoFiles)+len(p.XTestGoFiles) > 0 {
|
||||
pmain, ptest, pxtest, err := load.GetTestPackagesFor(p, nil)
|
||||
if err != nil {
|
||||
if *listE {
|
||||
pkgs = append(pkgs, &load.Package{
|
||||
PackagePublic: load.PackagePublic{
|
||||
ImportPath: p.ImportPath + ".test",
|
||||
Error: &load.PackageError{Err: err.Error()},
|
||||
},
|
||||
})
|
||||
continue
|
||||
}
|
||||
base.Errorf("can't load test package: %s", err)
|
||||
continue
|
||||
}
|
||||
pkgs = append(pkgs, pmain)
|
||||
if ptest != nil {
|
||||
pkgs = append(pkgs, ptest)
|
||||
}
|
||||
if pxtest != nil {
|
||||
pkgs = append(pkgs, pxtest)
|
||||
}
|
||||
|
||||
data := *pmain.Internal.TestmainGo
|
||||
h := cache.NewHash("testmain")
|
||||
h.Write([]byte("testmain\n"))
|
||||
h.Write(data)
|
||||
out, _, err := c.Put(h.Sum(), bytes.NewReader(data))
|
||||
if err != nil {
|
||||
base.Fatalf("%s", err)
|
||||
}
|
||||
pmain.GoFiles[0] = c.OutputFile(out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remember which packages are named on the command line.
|
||||
cmdline := make(map[*load.Package]bool)
|
||||
for _, p := range pkgs {
|
||||
cmdline[p] = true
|
||||
}
|
||||
|
||||
if *listDeps {
|
||||
// Note: This changes the order of the listed packages
|
||||
// from "as written on the command line" to
|
||||
// "a depth-first post-order traversal".
|
||||
// (The dependency exploration order for a given node
|
||||
// is alphabetical, same as listed in .Deps.)
|
||||
// Note that -deps is applied after -test,
|
||||
// so that you only get descriptions of tests for the things named
|
||||
// explicitly on the command line, not for all dependencies.
|
||||
pkgs = load.PackageList(pkgs)
|
||||
}
|
||||
|
||||
// Do we need to run a build to gather information?
|
||||
needStale := *listJson || strings.Contains(*listFmt, ".Stale")
|
||||
if needStale {
|
||||
if needStale || *listExport || *listCompiled {
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
b.ComputeStaleOnly = true
|
||||
b.IsCmdList = true
|
||||
b.NeedExport = *listExport
|
||||
b.NeedCompiledGoFiles = *listCompiled
|
||||
a := &work.Action{}
|
||||
// TODO: Use pkgsFilter?
|
||||
for _, p := range pkgs {
|
||||
a.Deps = append(a.Deps, b.AutoAction(work.ModeInstall, work.ModeInstall, p))
|
||||
if len(p.GoFiles)+len(p.CgoFiles) > 0 {
|
||||
a.Deps = append(a.Deps, b.AutoAction(work.ModeInstall, work.ModeInstall, p))
|
||||
}
|
||||
}
|
||||
b.Do(a)
|
||||
}
|
||||
|
||||
for _, pkg := range pkgs {
|
||||
for _, p := range pkgs {
|
||||
// Show vendor-expanded paths in listing
|
||||
pkg.TestImports = pkg.Resolve(pkg.TestImports)
|
||||
pkg.XTestImports = pkg.Resolve(pkg.XTestImports)
|
||||
p.TestImports = p.Resolve(p.TestImports)
|
||||
p.XTestImports = p.Resolve(p.XTestImports)
|
||||
p.DepOnly = !cmdline[p]
|
||||
|
||||
do(&pkg.PackagePublic)
|
||||
if *listCompiled {
|
||||
p.Imports = str.StringList(p.Imports, p.Internal.CompiledImports)
|
||||
}
|
||||
}
|
||||
|
||||
if *listTest {
|
||||
all := pkgs
|
||||
if !*listDeps {
|
||||
all = load.PackageList(pkgs)
|
||||
}
|
||||
// Update import paths to distinguish the real package p
|
||||
// from p recompiled for q.test.
|
||||
// This must happen only once the build code is done
|
||||
// looking at import paths, because it will get very confused
|
||||
// if it sees these.
|
||||
old := make(map[string]string)
|
||||
for _, p := range all {
|
||||
if p.ForTest != "" {
|
||||
new := p.ImportPath + " [" + p.ForTest + ".test]"
|
||||
old[new] = p.ImportPath
|
||||
p.ImportPath = new
|
||||
}
|
||||
p.DepOnly = !cmdline[p]
|
||||
}
|
||||
// Update import path lists to use new strings.
|
||||
m := make(map[string]string)
|
||||
for _, p := range all {
|
||||
for _, p1 := range p.Internal.Imports {
|
||||
if p1.ForTest != "" {
|
||||
m[old[p1.ImportPath]] = p1.ImportPath
|
||||
}
|
||||
}
|
||||
for i, old := range p.Imports {
|
||||
if new := m[old]; new != "" {
|
||||
p.Imports[i] = new
|
||||
}
|
||||
}
|
||||
for old := range m {
|
||||
delete(m, old)
|
||||
}
|
||||
}
|
||||
// Recompute deps lists using new strings, from the leaves up.
|
||||
for _, p := range all {
|
||||
deps := make(map[string]bool)
|
||||
for _, p1 := range p.Internal.Imports {
|
||||
deps[p1.ImportPath] = true
|
||||
for _, d := range p1.Deps {
|
||||
deps[d] = true
|
||||
}
|
||||
}
|
||||
p.Deps = make([]string, 0, len(deps))
|
||||
for d := range deps {
|
||||
p.Deps = append(p.Deps, d)
|
||||
}
|
||||
sort.Strings(p.Deps)
|
||||
}
|
||||
}
|
||||
|
||||
// Record non-identity import mappings in p.ImportMap.
|
||||
for _, p := range pkgs {
|
||||
for i, srcPath := range p.Internal.RawImports {
|
||||
path := p.Imports[i]
|
||||
if path != srcPath {
|
||||
if p.ImportMap == nil {
|
||||
p.ImportMap = make(map[string]string)
|
||||
}
|
||||
p.ImportMap[srcPath] = path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range pkgs {
|
||||
do(&p.PackagePublic)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -91,31 +91,3 @@ func (f *PerPackageFlag) For(p *Package) []string {
|
|||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
var cmdlineMatchers []func(*Package) bool
|
||||
|
||||
// SetCmdlinePatterns records the set of patterns given on the command line,
|
||||
// for use by the PerPackageFlags.
|
||||
func SetCmdlinePatterns(args []string) {
|
||||
setCmdlinePatterns(args, base.Cwd)
|
||||
}
|
||||
|
||||
func setCmdlinePatterns(args []string, cwd string) {
|
||||
if len(args) == 0 {
|
||||
args = []string{"."}
|
||||
}
|
||||
cmdlineMatchers = nil // allow reset for testing
|
||||
for _, arg := range args {
|
||||
cmdlineMatchers = append(cmdlineMatchers, MatchPackage(arg, cwd))
|
||||
}
|
||||
}
|
||||
|
||||
// isCmdlinePkg reports whether p is a package listed on the command line.
|
||||
func isCmdlinePkg(p *Package) bool {
|
||||
for _, m := range cmdlineMatchers {
|
||||
if m(p) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -32,22 +32,6 @@ func hasSubdir(root, dir string) (rel string, ok bool) {
|
|||
return filepath.ToSlash(dir[len(root):]), true
|
||||
}
|
||||
|
||||
// hasPathPrefix reports whether the path s begins with the
|
||||
// elements in prefix.
|
||||
func hasPathPrefix(s, prefix string) bool {
|
||||
switch {
|
||||
default:
|
||||
return false
|
||||
case len(s) == len(prefix):
|
||||
return s == prefix
|
||||
case len(s) > len(prefix):
|
||||
if prefix != "" && prefix[len(prefix)-1] == '/' {
|
||||
return strings.HasPrefix(s, prefix)
|
||||
}
|
||||
return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
|
||||
}
|
||||
}
|
||||
|
||||
// expandPath returns the symlink-expanded form of path.
|
||||
func expandPath(p string) string {
|
||||
x, err := filepath.EvalSymlinks(p)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,271 +5,16 @@
|
|||
package load
|
||||
|
||||
import (
|
||||
"cmd/go/internal/cfg"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"cmd/go/internal/search"
|
||||
)
|
||||
|
||||
// allPackages returns all the packages that can be found
|
||||
// under the $GOPATH directories and $GOROOT matching pattern.
|
||||
// The pattern is either "all" (all packages), "std" (standard packages),
|
||||
// "cmd" (standard commands), or a path including "...".
|
||||
func allPackages(pattern string) []string {
|
||||
pkgs := MatchPackages(pattern)
|
||||
if len(pkgs) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
|
||||
}
|
||||
return pkgs
|
||||
}
|
||||
|
||||
// allPackagesInFS is like allPackages but is passed a pattern
|
||||
// beginning ./ or ../, meaning it should scan the tree rooted
|
||||
// at the given directory. There are ... in the pattern too.
|
||||
func allPackagesInFS(pattern string) []string {
|
||||
pkgs := MatchPackagesInFS(pattern)
|
||||
if len(pkgs) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
|
||||
}
|
||||
return pkgs
|
||||
}
|
||||
|
||||
// MatchPackages returns a list of package paths matching pattern
|
||||
// (see go help packages for pattern syntax).
|
||||
func MatchPackages(pattern string) []string {
|
||||
match := func(string) bool { return true }
|
||||
treeCanMatch := func(string) bool { return true }
|
||||
if !IsMetaPackage(pattern) {
|
||||
match = matchPattern(pattern)
|
||||
treeCanMatch = treeCanMatchPattern(pattern)
|
||||
}
|
||||
|
||||
have := map[string]bool{
|
||||
"builtin": true, // ignore pseudo-package that exists only for documentation
|
||||
}
|
||||
if !cfg.BuildContext.CgoEnabled {
|
||||
have["runtime/cgo"] = true // ignore during walk
|
||||
}
|
||||
var pkgs []string
|
||||
|
||||
for _, src := range cfg.BuildContext.SrcDirs() {
|
||||
if (pattern == "std" || pattern == "cmd") && src != cfg.GOROOTsrc {
|
||||
continue
|
||||
}
|
||||
src = filepath.Clean(src) + string(filepath.Separator)
|
||||
root := src
|
||||
if pattern == "cmd" {
|
||||
root += "cmd" + string(filepath.Separator)
|
||||
}
|
||||
filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil || path == src {
|
||||
return nil
|
||||
}
|
||||
|
||||
want := true
|
||||
// Avoid .foo, _foo, and testdata directory trees.
|
||||
_, elem := filepath.Split(path)
|
||||
if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
|
||||
want = false
|
||||
}
|
||||
|
||||
name := filepath.ToSlash(path[len(src):])
|
||||
if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") {
|
||||
// The name "std" is only the standard library.
|
||||
// If the name is cmd, it's the root of the command tree.
|
||||
want = false
|
||||
}
|
||||
if !treeCanMatch(name) {
|
||||
want = false
|
||||
}
|
||||
|
||||
if !fi.IsDir() {
|
||||
if fi.Mode()&os.ModeSymlink != 0 && want {
|
||||
if target, err := os.Stat(path); err == nil && target.IsDir() {
|
||||
fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if !want {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
if have[name] {
|
||||
return nil
|
||||
}
|
||||
have[name] = true
|
||||
if !match(name) {
|
||||
return nil
|
||||
}
|
||||
pkg, err := cfg.BuildContext.ImportDir(path, 0)
|
||||
if err != nil {
|
||||
if _, noGo := err.(*build.NoGoError); noGo {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// If we are expanding "cmd", skip main
|
||||
// packages under cmd/vendor. At least as of
|
||||
// March, 2017, there is one there for the
|
||||
// vendored pprof tool.
|
||||
if pattern == "cmd" && strings.HasPrefix(pkg.ImportPath, "cmd/vendor") && pkg.Name == "main" {
|
||||
return nil
|
||||
}
|
||||
|
||||
pkgs = append(pkgs, name)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return pkgs
|
||||
}
|
||||
|
||||
// MatchPackagesInFS returns a list of package paths matching pattern,
|
||||
// which must begin with ./ or ../
|
||||
// (see go help packages for pattern syntax).
|
||||
func MatchPackagesInFS(pattern string) []string {
|
||||
// Find directory to begin the scan.
|
||||
// Could be smarter but this one optimization
|
||||
// is enough for now, since ... is usually at the
|
||||
// end of a path.
|
||||
i := strings.Index(pattern, "...")
|
||||
dir, _ := path.Split(pattern[:i])
|
||||
|
||||
// pattern begins with ./ or ../.
|
||||
// path.Clean will discard the ./ but not the ../.
|
||||
// We need to preserve the ./ for pattern matching
|
||||
// and in the returned import paths.
|
||||
prefix := ""
|
||||
if strings.HasPrefix(pattern, "./") {
|
||||
prefix = "./"
|
||||
}
|
||||
match := matchPattern(pattern)
|
||||
|
||||
var pkgs []string
|
||||
filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil || !fi.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if path == dir {
|
||||
// filepath.Walk starts at dir and recurses. For the recursive case,
|
||||
// the path is the result of filepath.Join, which calls filepath.Clean.
|
||||
// The initial case is not Cleaned, though, so we do this explicitly.
|
||||
//
|
||||
// This converts a path like "./io/" to "io". Without this step, running
|
||||
// "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io
|
||||
// package, because prepending the prefix "./" to the unclean path would
|
||||
// result in "././io", and match("././io") returns false.
|
||||
path = filepath.Clean(path)
|
||||
}
|
||||
|
||||
// Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..".
|
||||
_, elem := filepath.Split(path)
|
||||
dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".."
|
||||
if dot || strings.HasPrefix(elem, "_") || elem == "testdata" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
name := prefix + filepath.ToSlash(path)
|
||||
if !match(name) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// We keep the directory if we can import it, or if we can't import it
|
||||
// due to invalid Go source files. This means that directories containing
|
||||
// parse errors will be built (and fail) instead of being silently skipped
|
||||
// as not matching the pattern. Go 1.5 and earlier skipped, but that
|
||||
// behavior means people miss serious mistakes.
|
||||
// See golang.org/issue/11407.
|
||||
if p, err := cfg.BuildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) {
|
||||
if _, noGo := err.(*build.NoGoError); !noGo {
|
||||
log.Print(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
pkgs = append(pkgs, name)
|
||||
return nil
|
||||
})
|
||||
return pkgs
|
||||
}
|
||||
|
||||
// treeCanMatchPattern(pattern)(name) reports whether
|
||||
// name or children of name can possibly match pattern.
|
||||
// Pattern is the same limited glob accepted by matchPattern.
|
||||
func treeCanMatchPattern(pattern string) func(name string) bool {
|
||||
wildCard := false
|
||||
if i := strings.Index(pattern, "..."); i >= 0 {
|
||||
wildCard = true
|
||||
pattern = pattern[:i]
|
||||
}
|
||||
return func(name string) bool {
|
||||
return len(name) <= len(pattern) && hasPathPrefix(pattern, name) ||
|
||||
wildCard && strings.HasPrefix(name, pattern)
|
||||
}
|
||||
}
|
||||
|
||||
// matchPattern(pattern)(name) reports whether
|
||||
// name matches pattern. Pattern is a limited glob
|
||||
// pattern in which '...' means 'any string' and there
|
||||
// is no other special syntax.
|
||||
// Unfortunately, there are two special cases. Quoting "go help packages":
|
||||
//
|
||||
// First, /... at the end of the pattern can match an empty string,
|
||||
// so that net/... matches both net and packages in its subdirectories, like net/http.
|
||||
// Second, any slash-separted pattern element containing a wildcard never
|
||||
// participates in a match of the "vendor" element in the path of a vendored
|
||||
// package, so that ./... does not match packages in subdirectories of
|
||||
// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do.
|
||||
// Note, however, that a directory named vendor that itself contains code
|
||||
// is not a vendored package: cmd/vendor would be a command named vendor,
|
||||
// and the pattern cmd/... matches it.
|
||||
func matchPattern(pattern string) func(name string) bool {
|
||||
// Convert pattern to regular expression.
|
||||
// The strategy for the trailing /... is to nest it in an explicit ? expression.
|
||||
// The strategy for the vendor exclusion is to change the unmatchable
|
||||
// vendor strings to a disallowed code point (vendorChar) and to use
|
||||
// "(anything but that codepoint)*" as the implementation of the ... wildcard.
|
||||
// This is a bit complicated but the obvious alternative,
|
||||
// namely a hand-written search like in most shell glob matchers,
|
||||
// is too easy to make accidentally exponential.
|
||||
// Using package regexp guarantees linear-time matching.
|
||||
|
||||
const vendorChar = "\x00"
|
||||
|
||||
if strings.Contains(pattern, vendorChar) {
|
||||
return func(name string) bool { return false }
|
||||
}
|
||||
|
||||
re := regexp.QuoteMeta(pattern)
|
||||
re = replaceVendor(re, vendorChar)
|
||||
switch {
|
||||
case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`):
|
||||
re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)`
|
||||
case re == vendorChar+`/\.\.\.`:
|
||||
re = `(/vendor|/` + vendorChar + `/\.\.\.)`
|
||||
case strings.HasSuffix(re, `/\.\.\.`):
|
||||
re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?`
|
||||
}
|
||||
re = strings.Replace(re, `\.\.\.`, `[^`+vendorChar+`]*`, -1)
|
||||
|
||||
reg := regexp.MustCompile(`^` + re + `$`)
|
||||
|
||||
return func(name string) bool {
|
||||
if strings.Contains(name, vendorChar) {
|
||||
return false
|
||||
}
|
||||
return reg.MatchString(replaceVendor(name, vendorChar))
|
||||
}
|
||||
}
|
||||
|
||||
// MatchPackage(pattern, cwd)(p) reports whether package p matches pattern in the working directory cwd.
|
||||
func MatchPackage(pattern, cwd string) func(*Package) bool {
|
||||
switch {
|
||||
case strings.HasPrefix(pattern, "./") || strings.HasPrefix(pattern, "../") || pattern == "." || pattern == "..":
|
||||
case search.IsRelativePath(pattern):
|
||||
// Split pattern into leading pattern-free directory path
|
||||
// (including all . and .. elements) and the final pattern.
|
||||
var dir string
|
||||
|
@ -284,7 +29,7 @@ func MatchPackage(pattern, cwd string) func(*Package) bool {
|
|||
if pattern == "" {
|
||||
return func(p *Package) bool { return p.Dir == dir }
|
||||
}
|
||||
matchPath := matchPattern(pattern)
|
||||
matchPath := search.MatchPattern(pattern)
|
||||
return func(p *Package) bool {
|
||||
// Compute relative path to dir and see if it matches the pattern.
|
||||
rel, err := filepath.Rel(dir, p.Dir)
|
||||
|
@ -305,81 +50,7 @@ func MatchPackage(pattern, cwd string) func(*Package) bool {
|
|||
case pattern == "cmd":
|
||||
return func(p *Package) bool { return p.Standard && strings.HasPrefix(p.ImportPath, "cmd/") }
|
||||
default:
|
||||
matchPath := matchPattern(pattern)
|
||||
matchPath := search.MatchPattern(pattern)
|
||||
return func(p *Package) bool { return matchPath(p.ImportPath) }
|
||||
}
|
||||
}
|
||||
|
||||
// replaceVendor returns the result of replacing
|
||||
// non-trailing vendor path elements in x with repl.
|
||||
func replaceVendor(x, repl string) string {
|
||||
if !strings.Contains(x, "vendor") {
|
||||
return x
|
||||
}
|
||||
elem := strings.Split(x, "/")
|
||||
for i := 0; i < len(elem)-1; i++ {
|
||||
if elem[i] == "vendor" {
|
||||
elem[i] = repl
|
||||
}
|
||||
}
|
||||
return strings.Join(elem, "/")
|
||||
}
|
||||
|
||||
// ImportPaths returns the import paths to use for the given command line.
|
||||
func ImportPaths(args []string) []string {
|
||||
args = ImportPathsNoDotExpansion(args)
|
||||
var out []string
|
||||
for _, a := range args {
|
||||
if strings.Contains(a, "...") {
|
||||
if build.IsLocalImport(a) {
|
||||
out = append(out, allPackagesInFS(a)...)
|
||||
} else {
|
||||
out = append(out, allPackages(a)...)
|
||||
}
|
||||
continue
|
||||
}
|
||||
out = append(out, a)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ImportPathsNoDotExpansion returns the import paths to use for the given
|
||||
// command line, but it does no ... expansion.
|
||||
func ImportPathsNoDotExpansion(args []string) []string {
|
||||
if cmdlineMatchers == nil {
|
||||
SetCmdlinePatterns(args)
|
||||
}
|
||||
if len(args) == 0 {
|
||||
return []string{"."}
|
||||
}
|
||||
var out []string
|
||||
for _, a := range args {
|
||||
// Arguments are supposed to be import paths, but
|
||||
// as a courtesy to Windows developers, rewrite \ to /
|
||||
// in command-line arguments. Handles .\... and so on.
|
||||
if filepath.Separator == '\\' {
|
||||
a = strings.Replace(a, `\`, `/`, -1)
|
||||
}
|
||||
|
||||
// Put argument in canonical form, but preserve leading ./.
|
||||
if strings.HasPrefix(a, "./") {
|
||||
a = "./" + path.Clean(a)
|
||||
if a == "./." {
|
||||
a = "."
|
||||
}
|
||||
} else {
|
||||
a = path.Clean(a)
|
||||
}
|
||||
if IsMetaPackage(a) {
|
||||
out = append(out, allPackages(a)...)
|
||||
continue
|
||||
}
|
||||
out = append(out, a)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// IsMetaPackage checks if name is a reserved package name that expands to multiple packages.
|
||||
func IsMetaPackage(name string) bool {
|
||||
return name == "std" || name == "cmd" || name == "all"
|
||||
}
|
||||
|
|
654
libgo/go/cmd/go/internal/load/test.go
Normal file
654
libgo/go/cmd/go/internal/load/test.go
Normal file
|
@ -0,0 +1,654 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package load
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/str"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/doc"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var TestMainDeps = []string{
|
||||
// Dependencies for testmain.
|
||||
"os",
|
||||
"testing",
|
||||
"testing/internal/testdeps",
|
||||
}
|
||||
|
||||
type TestCover struct {
|
||||
Mode string
|
||||
Local bool
|
||||
Pkgs []*Package
|
||||
Paths []string
|
||||
Vars []coverInfo
|
||||
DeclVars func(*Package, ...string) map[string]*CoverVar
|
||||
}
|
||||
|
||||
// TestPackagesFor returns three packages:
|
||||
// - ptest, the package p compiled with added "package p" test files.
|
||||
// - pxtest, the result of compiling any "package p_test" (external) test files.
|
||||
// - pmain, the package main corresponding to the test binary (running tests in ptest and pxtest).
|
||||
//
|
||||
// If the package has no "package p_test" test files, pxtest will be nil.
|
||||
// If the non-test compilation of package p can be reused
|
||||
// (for example, if there are no "package p" test files and
|
||||
// package p need not be instrumented for coverage or any other reason),
|
||||
// then the returned ptest == p.
|
||||
//
|
||||
// The caller is expected to have checked that len(p.TestGoFiles)+len(p.XTestGoFiles) > 0,
|
||||
// or else there's no point in any of this.
|
||||
func GetTestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Package, err error) {
|
||||
var imports, ximports []*Package
|
||||
var stk ImportStack
|
||||
stk.Push(p.ImportPath + " (test)")
|
||||
rawTestImports := str.StringList(p.TestImports)
|
||||
for i, path := range p.TestImports {
|
||||
p1 := LoadImport(path, p.Dir, p, &stk, p.Internal.Build.TestImportPos[path], ResolveImport)
|
||||
if p1.Error != nil {
|
||||
return nil, nil, nil, p1.Error
|
||||
}
|
||||
if len(p1.DepsErrors) > 0 {
|
||||
err := p1.DepsErrors[0]
|
||||
err.Pos = "" // show full import stack
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if str.Contains(p1.Deps, p.ImportPath) || p1.ImportPath == p.ImportPath {
|
||||
// Same error that loadPackage returns (via reusePackage) in pkg.go.
|
||||
// Can't change that code, because that code is only for loading the
|
||||
// non-test copy of a package.
|
||||
err := &PackageError{
|
||||
ImportStack: testImportStack(stk[0], p1, p.ImportPath),
|
||||
Err: "import cycle not allowed in test",
|
||||
IsImportCycle: true,
|
||||
}
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
p.TestImports[i] = p1.ImportPath
|
||||
imports = append(imports, p1)
|
||||
}
|
||||
stk.Pop()
|
||||
stk.Push(p.ImportPath + "_test")
|
||||
pxtestNeedsPtest := false
|
||||
rawXTestImports := str.StringList(p.XTestImports)
|
||||
for i, path := range p.XTestImports {
|
||||
p1 := LoadImport(path, p.Dir, p, &stk, p.Internal.Build.XTestImportPos[path], ResolveImport)
|
||||
if p1.Error != nil {
|
||||
return nil, nil, nil, p1.Error
|
||||
}
|
||||
if len(p1.DepsErrors) > 0 {
|
||||
err := p1.DepsErrors[0]
|
||||
err.Pos = "" // show full import stack
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if p1.ImportPath == p.ImportPath {
|
||||
pxtestNeedsPtest = true
|
||||
} else {
|
||||
ximports = append(ximports, p1)
|
||||
}
|
||||
p.XTestImports[i] = p1.ImportPath
|
||||
}
|
||||
stk.Pop()
|
||||
|
||||
// Test package.
|
||||
if len(p.TestGoFiles) > 0 || p.Name == "main" || cover != nil && cover.Local {
|
||||
ptest = new(Package)
|
||||
*ptest = *p
|
||||
ptest.ForTest = p.ImportPath
|
||||
ptest.GoFiles = nil
|
||||
ptest.GoFiles = append(ptest.GoFiles, p.GoFiles...)
|
||||
ptest.GoFiles = append(ptest.GoFiles, p.TestGoFiles...)
|
||||
ptest.Target = ""
|
||||
// Note: The preparation of the vet config requires that common
|
||||
// indexes in ptest.Imports and ptest.Internal.RawImports
|
||||
// all line up (but RawImports can be shorter than the others).
|
||||
// That is, for 0 ≤ i < len(RawImports),
|
||||
// RawImports[i] is the import string in the program text, and
|
||||
// Imports[i] is the expanded import string (vendoring applied or relative path expanded away).
|
||||
// Any implicitly added imports appear in Imports and Internal.Imports
|
||||
// but not RawImports (because they were not in the source code).
|
||||
// We insert TestImports, imports, and rawTestImports at the start of
|
||||
// these lists to preserve the alignment.
|
||||
// Note that p.Internal.Imports may not be aligned with p.Imports/p.Internal.RawImports,
|
||||
// but we insert at the beginning there too just for consistency.
|
||||
ptest.Imports = str.StringList(p.TestImports, p.Imports)
|
||||
ptest.Internal.Imports = append(imports, p.Internal.Imports...)
|
||||
ptest.Internal.RawImports = str.StringList(rawTestImports, p.Internal.RawImports)
|
||||
ptest.Internal.ForceLibrary = true
|
||||
ptest.Internal.Build = new(build.Package)
|
||||
*ptest.Internal.Build = *p.Internal.Build
|
||||
m := map[string][]token.Position{}
|
||||
for k, v := range p.Internal.Build.ImportPos {
|
||||
m[k] = append(m[k], v...)
|
||||
}
|
||||
for k, v := range p.Internal.Build.TestImportPos {
|
||||
m[k] = append(m[k], v...)
|
||||
}
|
||||
ptest.Internal.Build.ImportPos = m
|
||||
} else {
|
||||
ptest = p
|
||||
}
|
||||
|
||||
// External test package.
|
||||
if len(p.XTestGoFiles) > 0 {
|
||||
pxtest = &Package{
|
||||
PackagePublic: PackagePublic{
|
||||
Name: p.Name + "_test",
|
||||
ImportPath: p.ImportPath + "_test",
|
||||
Root: p.Root,
|
||||
Dir: p.Dir,
|
||||
GoFiles: p.XTestGoFiles,
|
||||
Imports: p.XTestImports,
|
||||
ForTest: p.ImportPath,
|
||||
},
|
||||
Internal: PackageInternal{
|
||||
LocalPrefix: p.Internal.LocalPrefix,
|
||||
Build: &build.Package{
|
||||
ImportPos: p.Internal.Build.XTestImportPos,
|
||||
},
|
||||
Imports: ximports,
|
||||
RawImports: rawXTestImports,
|
||||
|
||||
Asmflags: p.Internal.Asmflags,
|
||||
Gcflags: p.Internal.Gcflags,
|
||||
Ldflags: p.Internal.Ldflags,
|
||||
Gccgoflags: p.Internal.Gccgoflags,
|
||||
},
|
||||
}
|
||||
if pxtestNeedsPtest {
|
||||
pxtest.Internal.Imports = append(pxtest.Internal.Imports, ptest)
|
||||
}
|
||||
}
|
||||
|
||||
// Build main package.
|
||||
pmain = &Package{
|
||||
PackagePublic: PackagePublic{
|
||||
Name: "main",
|
||||
Dir: p.Dir,
|
||||
GoFiles: []string{"_testmain.go"},
|
||||
ImportPath: p.ImportPath + ".test",
|
||||
Root: p.Root,
|
||||
Imports: str.StringList(TestMainDeps),
|
||||
},
|
||||
Internal: PackageInternal{
|
||||
Build: &build.Package{Name: "main"},
|
||||
Asmflags: p.Internal.Asmflags,
|
||||
Gcflags: p.Internal.Gcflags,
|
||||
Ldflags: p.Internal.Ldflags,
|
||||
Gccgoflags: p.Internal.Gccgoflags,
|
||||
},
|
||||
}
|
||||
|
||||
// The generated main also imports testing, regexp, and os.
|
||||
// Also the linker introduces implicit dependencies reported by LinkerDeps.
|
||||
stk.Push("testmain")
|
||||
deps := TestMainDeps // cap==len, so safe for append
|
||||
for _, d := range LinkerDeps(p) {
|
||||
deps = append(deps, d)
|
||||
}
|
||||
for _, dep := range deps {
|
||||
if dep == ptest.ImportPath {
|
||||
pmain.Internal.Imports = append(pmain.Internal.Imports, ptest)
|
||||
} else {
|
||||
p1 := LoadImport(dep, "", nil, &stk, nil, 0)
|
||||
if p1.Error != nil {
|
||||
return nil, nil, nil, p1.Error
|
||||
}
|
||||
pmain.Internal.Imports = append(pmain.Internal.Imports, p1)
|
||||
}
|
||||
}
|
||||
stk.Pop()
|
||||
|
||||
if cover != nil && cover.Pkgs != nil {
|
||||
// Add imports, but avoid duplicates.
|
||||
seen := map[*Package]bool{p: true, ptest: true}
|
||||
for _, p1 := range pmain.Internal.Imports {
|
||||
seen[p1] = true
|
||||
}
|
||||
for _, p1 := range cover.Pkgs {
|
||||
if !seen[p1] {
|
||||
seen[p1] = true
|
||||
pmain.Internal.Imports = append(pmain.Internal.Imports, p1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do initial scan for metadata needed for writing _testmain.go
|
||||
// Use that metadata to update the list of imports for package main.
|
||||
// The list of imports is used by recompileForTest and by the loop
|
||||
// afterward that gathers t.Cover information.
|
||||
t, err := loadTestFuncs(ptest)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
t.Cover = cover
|
||||
if len(ptest.GoFiles)+len(ptest.CgoFiles) > 0 {
|
||||
pmain.Internal.Imports = append(pmain.Internal.Imports, ptest)
|
||||
pmain.Imports = append(pmain.Imports, ptest.ImportPath)
|
||||
t.ImportTest = true
|
||||
}
|
||||
if pxtest != nil {
|
||||
pmain.Internal.Imports = append(pmain.Internal.Imports, pxtest)
|
||||
pmain.Imports = append(pmain.Imports, pxtest.ImportPath)
|
||||
t.ImportXtest = true
|
||||
}
|
||||
|
||||
// Sort and dedup pmain.Imports.
|
||||
// Only matters for go list -test output.
|
||||
sort.Strings(pmain.Imports)
|
||||
w := 0
|
||||
for _, path := range pmain.Imports {
|
||||
if w == 0 || path != pmain.Imports[w-1] {
|
||||
pmain.Imports[w] = path
|
||||
w++
|
||||
}
|
||||
}
|
||||
pmain.Imports = pmain.Imports[:w]
|
||||
pmain.Internal.RawImports = str.StringList(pmain.Imports)
|
||||
|
||||
if ptest != p {
|
||||
// We have made modifications to the package p being tested
|
||||
// and are rebuilding p (as ptest).
|
||||
// Arrange to rebuild all packages q such that
|
||||
// the test depends on q and q depends on p.
|
||||
// This makes sure that q sees the modifications to p.
|
||||
// Strictly speaking, the rebuild is only necessary if the
|
||||
// modifications to p change its export metadata, but
|
||||
// determining that is a bit tricky, so we rebuild always.
|
||||
recompileForTest(pmain, p, ptest, pxtest)
|
||||
}
|
||||
|
||||
// Should we apply coverage analysis locally,
|
||||
// only for this package and only for this test?
|
||||
// Yes, if -cover is on but -coverpkg has not specified
|
||||
// a list of packages for global coverage.
|
||||
if cover != nil && cover.Local {
|
||||
ptest.Internal.CoverMode = cover.Mode
|
||||
var coverFiles []string
|
||||
coverFiles = append(coverFiles, ptest.GoFiles...)
|
||||
coverFiles = append(coverFiles, ptest.CgoFiles...)
|
||||
ptest.Internal.CoverVars = cover.DeclVars(ptest, coverFiles...)
|
||||
}
|
||||
|
||||
for _, cp := range pmain.Internal.Imports {
|
||||
if len(cp.Internal.CoverVars) > 0 {
|
||||
t.Cover.Vars = append(t.Cover.Vars, coverInfo{cp, cp.Internal.CoverVars})
|
||||
}
|
||||
}
|
||||
|
||||
data, err := formatTestmain(t)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
pmain.Internal.TestmainGo = &data
|
||||
|
||||
return pmain, ptest, pxtest, nil
|
||||
}
|
||||
|
||||
func testImportStack(top string, p *Package, target string) []string {
|
||||
stk := []string{top, p.ImportPath}
|
||||
Search:
|
||||
for p.ImportPath != target {
|
||||
for _, p1 := range p.Internal.Imports {
|
||||
if p1.ImportPath == target || str.Contains(p1.Deps, target) {
|
||||
stk = append(stk, p1.ImportPath)
|
||||
p = p1
|
||||
continue Search
|
||||
}
|
||||
}
|
||||
// Can't happen, but in case it does...
|
||||
stk = append(stk, "<lost path to cycle>")
|
||||
break
|
||||
}
|
||||
return stk
|
||||
}
|
||||
|
||||
func recompileForTest(pmain, preal, ptest, pxtest *Package) {
|
||||
// The "test copy" of preal is ptest.
|
||||
// For each package that depends on preal, make a "test copy"
|
||||
// that depends on ptest. And so on, up the dependency tree.
|
||||
testCopy := map[*Package]*Package{preal: ptest}
|
||||
for _, p := range PackageList([]*Package{pmain}) {
|
||||
if p == preal {
|
||||
continue
|
||||
}
|
||||
// Copy on write.
|
||||
didSplit := p == pmain || p == pxtest
|
||||
split := func() {
|
||||
if didSplit {
|
||||
return
|
||||
}
|
||||
didSplit = true
|
||||
if testCopy[p] != nil {
|
||||
panic("recompileForTest loop")
|
||||
}
|
||||
p1 := new(Package)
|
||||
testCopy[p] = p1
|
||||
*p1 = *p
|
||||
p1.ForTest = preal.ImportPath
|
||||
p1.Internal.Imports = make([]*Package, len(p.Internal.Imports))
|
||||
copy(p1.Internal.Imports, p.Internal.Imports)
|
||||
p1.Imports = make([]string, len(p.Imports))
|
||||
copy(p1.Imports, p.Imports)
|
||||
p = p1
|
||||
p.Target = ""
|
||||
}
|
||||
|
||||
// Update p.Internal.Imports to use test copies.
|
||||
for i, imp := range p.Internal.Imports {
|
||||
if p1 := testCopy[imp]; p1 != nil && p1 != imp {
|
||||
split()
|
||||
p.Internal.Imports[i] = p1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// isTestFunc tells whether fn has the type of a testing function. arg
|
||||
// specifies the parameter type we look for: B, M or T.
|
||||
func isTestFunc(fn *ast.FuncDecl, arg string) bool {
|
||||
if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
|
||||
fn.Type.Params.List == nil ||
|
||||
len(fn.Type.Params.List) != 1 ||
|
||||
len(fn.Type.Params.List[0].Names) > 1 {
|
||||
return false
|
||||
}
|
||||
ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
// We can't easily check that the type is *testing.M
|
||||
// because we don't know how testing has been imported,
|
||||
// but at least check that it's *M or *something.M.
|
||||
// Same applies for B and T.
|
||||
if name, ok := ptr.X.(*ast.Ident); ok && name.Name == arg {
|
||||
return true
|
||||
}
|
||||
if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == arg {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isTest tells whether name looks like a test (or benchmark, according to prefix).
|
||||
// It is a Test (say) if there is a character after Test that is not a lower-case letter.
|
||||
// We don't want TesticularCancer.
|
||||
func isTest(name, prefix string) bool {
|
||||
if !strings.HasPrefix(name, prefix) {
|
||||
return false
|
||||
}
|
||||
if len(name) == len(prefix) { // "Test" is ok
|
||||
return true
|
||||
}
|
||||
rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
|
||||
return !unicode.IsLower(rune)
|
||||
}
|
||||
|
||||
type coverInfo struct {
|
||||
Package *Package
|
||||
Vars map[string]*CoverVar
|
||||
}
|
||||
|
||||
// loadTestFuncs returns the testFuncs describing the tests that will be run.
|
||||
func loadTestFuncs(ptest *Package) (*testFuncs, error) {
|
||||
t := &testFuncs{
|
||||
Package: ptest,
|
||||
}
|
||||
for _, file := range ptest.TestGoFiles {
|
||||
if err := t.load(filepath.Join(ptest.Dir, file), "_test", &t.ImportTest, &t.NeedTest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, file := range ptest.XTestGoFiles {
|
||||
if err := t.load(filepath.Join(ptest.Dir, file), "_xtest", &t.ImportXtest, &t.NeedXtest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// formatTestmain returns the content of the _testmain.go file for t.
|
||||
func formatTestmain(t *testFuncs) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
if err := testmainTmpl.Execute(&buf, t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
type testFuncs struct {
|
||||
Tests []testFunc
|
||||
Benchmarks []testFunc
|
||||
Examples []testFunc
|
||||
TestMain *testFunc
|
||||
Package *Package
|
||||
ImportTest bool
|
||||
NeedTest bool
|
||||
ImportXtest bool
|
||||
NeedXtest bool
|
||||
Cover *TestCover
|
||||
}
|
||||
|
||||
// ImportPath returns the import path of the package being tested, if it is within GOPATH.
|
||||
// This is printed by the testing package when running benchmarks.
|
||||
func (t *testFuncs) ImportPath() string {
|
||||
pkg := t.Package.ImportPath
|
||||
if strings.HasPrefix(pkg, "_/") {
|
||||
return ""
|
||||
}
|
||||
if pkg == "command-line-arguments" {
|
||||
return ""
|
||||
}
|
||||
return pkg
|
||||
}
|
||||
|
||||
// Covered returns a string describing which packages are being tested for coverage.
|
||||
// If the covered package is the same as the tested package, it returns the empty string.
|
||||
// Otherwise it is a comma-separated human-readable list of packages beginning with
|
||||
// " in", ready for use in the coverage message.
|
||||
func (t *testFuncs) Covered() string {
|
||||
if t.Cover == nil || t.Cover.Paths == nil {
|
||||
return ""
|
||||
}
|
||||
return " in " + strings.Join(t.Cover.Paths, ", ")
|
||||
}
|
||||
|
||||
// Tested returns the name of the package being tested.
|
||||
func (t *testFuncs) Tested() string {
|
||||
return t.Package.Name
|
||||
}
|
||||
|
||||
type testFunc struct {
|
||||
Package string // imported package name (_test or _xtest)
|
||||
Name string // function name
|
||||
Output string // output, for examples
|
||||
Unordered bool // output is allowed to be unordered.
|
||||
}
|
||||
|
||||
var testFileSet = token.NewFileSet()
|
||||
|
||||
func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
|
||||
f, err := parser.ParseFile(testFileSet, filename, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
return base.ExpandScanner(err)
|
||||
}
|
||||
for _, d := range f.Decls {
|
||||
n, ok := d.(*ast.FuncDecl)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if n.Recv != nil {
|
||||
continue
|
||||
}
|
||||
name := n.Name.String()
|
||||
switch {
|
||||
case name == "TestMain":
|
||||
if isTestFunc(n, "T") {
|
||||
t.Tests = append(t.Tests, testFunc{pkg, name, "", false})
|
||||
*doImport, *seen = true, true
|
||||
continue
|
||||
}
|
||||
err := checkTestFunc(n, "M")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if t.TestMain != nil {
|
||||
return errors.New("multiple definitions of TestMain")
|
||||
}
|
||||
t.TestMain = &testFunc{pkg, name, "", false}
|
||||
*doImport, *seen = true, true
|
||||
case isTest(name, "Test"):
|
||||
err := checkTestFunc(n, "T")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.Tests = append(t.Tests, testFunc{pkg, name, "", false})
|
||||
*doImport, *seen = true, true
|
||||
case isTest(name, "Benchmark"):
|
||||
err := checkTestFunc(n, "B")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false})
|
||||
*doImport, *seen = true, true
|
||||
}
|
||||
}
|
||||
ex := doc.Examples(f)
|
||||
sort.Slice(ex, func(i, j int) bool { return ex[i].Order < ex[j].Order })
|
||||
for _, e := range ex {
|
||||
*doImport = true // import test file whether executed or not
|
||||
if e.Output == "" && !e.EmptyOutput {
|
||||
// Don't run examples with no output.
|
||||
continue
|
||||
}
|
||||
t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output, e.Unordered})
|
||||
*seen = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkTestFunc(fn *ast.FuncDecl, arg string) error {
|
||||
if !isTestFunc(fn, arg) {
|
||||
name := fn.Name.String()
|
||||
pos := testFileSet.Position(fn.Pos())
|
||||
return fmt.Errorf("%s: wrong signature for %s, must be: func %s(%s *testing.%s)", pos, name, name, strings.ToLower(arg), arg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var testmainTmpl = template.Must(template.New("main").Parse(`
|
||||
package main
|
||||
|
||||
import (
|
||||
{{if not .TestMain}}
|
||||
"os"
|
||||
{{end}}
|
||||
"testing"
|
||||
"testing/internal/testdeps"
|
||||
|
||||
{{if .ImportTest}}
|
||||
{{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}}
|
||||
{{end}}
|
||||
{{if .ImportXtest}}
|
||||
{{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}}
|
||||
{{end}}
|
||||
{{if .Cover}}
|
||||
{{range $i, $p := .Cover.Vars}}
|
||||
_cover{{$i}} {{$p.Package.ImportPath | printf "%q"}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
)
|
||||
|
||||
var tests = []testing.InternalTest{
|
||||
{{range .Tests}}
|
||||
{"{{.Name}}", {{.Package}}.{{.Name}}},
|
||||
{{end}}
|
||||
}
|
||||
|
||||
var benchmarks = []testing.InternalBenchmark{
|
||||
{{range .Benchmarks}}
|
||||
{"{{.Name}}", {{.Package}}.{{.Name}}},
|
||||
{{end}}
|
||||
}
|
||||
|
||||
var examples = []testing.InternalExample{
|
||||
{{range .Examples}}
|
||||
{"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}},
|
||||
{{end}}
|
||||
}
|
||||
|
||||
func init() {
|
||||
testdeps.ImportPath = {{.ImportPath | printf "%q"}}
|
||||
}
|
||||
|
||||
{{if .Cover}}
|
||||
|
||||
// Only updated by init functions, so no need for atomicity.
|
||||
var (
|
||||
coverCounters = make(map[string][]uint32)
|
||||
coverBlocks = make(map[string][]testing.CoverBlock)
|
||||
)
|
||||
|
||||
func init() {
|
||||
{{range $i, $p := .Cover.Vars}}
|
||||
{{range $file, $cover := $p.Vars}}
|
||||
coverRegisterFile({{printf "%q" $cover.File}}, _cover{{$i}}.{{$cover.Var}}.Count[:], _cover{{$i}}.{{$cover.Var}}.Pos[:], _cover{{$i}}.{{$cover.Var}}.NumStmt[:])
|
||||
{{end}}
|
||||
{{end}}
|
||||
}
|
||||
|
||||
func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) {
|
||||
if 3*len(counter) != len(pos) || len(counter) != len(numStmts) {
|
||||
panic("coverage: mismatched sizes")
|
||||
}
|
||||
if coverCounters[fileName] != nil {
|
||||
// Already registered.
|
||||
return
|
||||
}
|
||||
coverCounters[fileName] = counter
|
||||
block := make([]testing.CoverBlock, len(counter))
|
||||
for i := range counter {
|
||||
block[i] = testing.CoverBlock{
|
||||
Line0: pos[3*i+0],
|
||||
Col0: uint16(pos[3*i+2]),
|
||||
Line1: pos[3*i+1],
|
||||
Col1: uint16(pos[3*i+2]>>16),
|
||||
Stmts: numStmts[i],
|
||||
}
|
||||
}
|
||||
coverBlocks[fileName] = block
|
||||
}
|
||||
{{end}}
|
||||
|
||||
func main() {
|
||||
{{if .Cover}}
|
||||
testing.RegisterCover(testing.Cover{
|
||||
Mode: {{printf "%q" .Cover.Mode}},
|
||||
Counters: coverCounters,
|
||||
Blocks: coverBlocks,
|
||||
CoveredPackages: {{printf "%q" .Covered}},
|
||||
})
|
||||
{{end}}
|
||||
m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, examples)
|
||||
{{with .TestMain}}
|
||||
{{.Package}}.{{.Name}}(m)
|
||||
{{else}}
|
||||
os.Exit(m.Run())
|
||||
{{end}}
|
||||
}
|
||||
|
||||
`))
|
133
libgo/go/cmd/go/internal/modcmd/download.go
Normal file
133
libgo/go/cmd/go/internal/modcmd/download.go
Normal file
|
@ -0,0 +1,133 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package modcmd
|
||||
|
||||
import (
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/par"
|
||||
"encoding/json"
|
||||
"os"
|
||||
)
|
||||
|
||||
var cmdDownload = &base.Command{
|
||||
UsageLine: "go mod download [-dir] [-json] [modules]",
|
||||
Short: "download modules to local cache",
|
||||
Long: `
|
||||
Download downloads the named modules, which can be module patterns selecting
|
||||
dependencies of the main module or module queries of the form path@version.
|
||||
With no arguments, download applies to all dependencies of the main module.
|
||||
|
||||
The go command will automatically download modules as needed during ordinary
|
||||
execution. The "go mod download" command is useful mainly for pre-filling
|
||||
the local cache or to compute the answers for a Go module proxy.
|
||||
|
||||
By default, download reports errors to standard error but is otherwise silent.
|
||||
The -json flag causes download to print a sequence of JSON objects
|
||||
to standard output, describing each downloaded module (or failure),
|
||||
corresponding to this Go struct:
|
||||
|
||||
type Module struct {
|
||||
Path string // module path
|
||||
Version string // module version
|
||||
Error string // error loading module
|
||||
Info string // absolute path to cached .info file
|
||||
GoMod string // absolute path to cached .mod file
|
||||
Zip string // absolute path to cached .zip file
|
||||
Dir string // absolute path to cached source root directory
|
||||
Sum string // checksum for path, version (as in go.sum)
|
||||
GoModSum string // checksum for go.mod (as in go.sum)
|
||||
}
|
||||
|
||||
See 'go help modules' for more about module queries.
|
||||
`,
|
||||
}
|
||||
|
||||
var downloadJSON = cmdDownload.Flag.Bool("json", false, "")
|
||||
|
||||
func init() {
|
||||
cmdDownload.Run = runDownload // break init cycle
|
||||
}
|
||||
|
||||
type moduleJSON struct {
|
||||
Path string `json:",omitempty"`
|
||||
Version string `json:",omitempty"`
|
||||
Error string `json:",omitempty"`
|
||||
Info string `json:",omitempty"`
|
||||
GoMod string `json:",omitempty"`
|
||||
Zip string `json:",omitempty"`
|
||||
Dir string `json:",omitempty"`
|
||||
Sum string `json:",omitempty"`
|
||||
GoModSum string `json:",omitempty"`
|
||||
}
|
||||
|
||||
func runDownload(cmd *base.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
args = []string{"all"}
|
||||
}
|
||||
|
||||
var mods []*moduleJSON
|
||||
var work par.Work
|
||||
listU := false
|
||||
listVersions := false
|
||||
for _, info := range modload.ListModules(args, listU, listVersions) {
|
||||
if info.Replace != nil {
|
||||
info = info.Replace
|
||||
}
|
||||
if info.Version == "" {
|
||||
continue
|
||||
}
|
||||
m := &moduleJSON{
|
||||
Path: info.Path,
|
||||
Version: info.Version,
|
||||
}
|
||||
mods = append(mods, m)
|
||||
work.Add(m)
|
||||
}
|
||||
|
||||
work.Do(10, func(item interface{}) {
|
||||
m := item.(*moduleJSON)
|
||||
var err error
|
||||
m.Info, err = modfetch.InfoFile(m.Path, m.Version)
|
||||
if err != nil {
|
||||
m.Error = err.Error()
|
||||
return
|
||||
}
|
||||
m.GoMod, err = modfetch.GoModFile(m.Path, m.Version)
|
||||
if err != nil {
|
||||
m.Error = err.Error()
|
||||
return
|
||||
}
|
||||
m.GoModSum, err = modfetch.GoModSum(m.Path, m.Version)
|
||||
if err != nil {
|
||||
m.Error = err.Error()
|
||||
return
|
||||
}
|
||||
mod := module.Version{Path: m.Path, Version: m.Version}
|
||||
m.Zip, err = modfetch.DownloadZip(mod)
|
||||
if err != nil {
|
||||
m.Error = err.Error()
|
||||
return
|
||||
}
|
||||
m.Sum = modfetch.Sum(mod)
|
||||
m.Dir, err = modfetch.Download(mod)
|
||||
if err != nil {
|
||||
m.Error = err.Error()
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
if *downloadJSON {
|
||||
for _, m := range mods {
|
||||
b, err := json.MarshalIndent(m, "", "\t")
|
||||
if err != nil {
|
||||
base.Fatalf("%v", err)
|
||||
}
|
||||
os.Stdout.Write(append(b, '\n'))
|
||||
}
|
||||
}
|
||||
}
|
382
libgo/go/cmd/go/internal/modcmd/edit.go
Normal file
382
libgo/go/cmd/go/internal/modcmd/edit.go
Normal file
|
@ -0,0 +1,382 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// go mod edit
|
||||
|
||||
package modcmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
)
|
||||
|
||||
var cmdEdit = &base.Command{
|
||||
UsageLine: "go mod edit [editing flags] [go.mod]",
|
||||
Short: "edit go.mod from tools or scripts",
|
||||
Long: `
|
||||
Edit provides a command-line interface for editing go.mod,
|
||||
for use primarily by tools or scripts. It reads only go.mod;
|
||||
it does not look up information about the modules involved.
|
||||
By default, edit reads and writes the go.mod file of the main module,
|
||||
but a different target file can be specified after the editing flags.
|
||||
|
||||
The editing flags specify a sequence of editing operations.
|
||||
|
||||
The -fmt flag reformats the go.mod file without making other changes.
|
||||
This reformatting is also implied by any other modifications that use or
|
||||
rewrite the go.mod file. The only time this flag is needed is if no other
|
||||
flags are specified, as in 'go mod edit -fmt'.
|
||||
|
||||
The -module flag changes the module's path (the go.mod file's module line).
|
||||
|
||||
The -require=path@version and -droprequire=path flags
|
||||
add and drop a requirement on the given module path and version.
|
||||
Note that -require overrides any existing requirements on path.
|
||||
These flags are mainly for tools that understand the module graph.
|
||||
Users should prefer 'go get path@version' or 'go get path@none',
|
||||
which make other go.mod adjustments as needed to satisfy
|
||||
constraints imposed by other modules.
|
||||
|
||||
The -exclude=path@version and -dropexclude=path@version flags
|
||||
add and drop an exclusion for the given module path and version.
|
||||
Note that -exclude=path@version is a no-op if that exclusion already exists.
|
||||
|
||||
The -replace=old[@v]=new[@v] and -dropreplace=old[@v] flags
|
||||
add and drop a replacement of the given module path and version pair.
|
||||
If the @v in old@v is omitted, the replacement applies to all versions
|
||||
with the old module path. If the @v in new@v is omitted, the new path
|
||||
should be a local module root directory, not a module path.
|
||||
Note that -replace overrides any existing replacements for old[@v].
|
||||
|
||||
The -require, -droprequire, -exclude, -dropexclude, -replace,
|
||||
and -dropreplace editing flags may be repeated, and the changes
|
||||
are applied in the order given.
|
||||
|
||||
The -print flag prints the final go.mod in its text format instead of
|
||||
writing it back to go.mod.
|
||||
|
||||
The -json flag prints the final go.mod file in JSON format instead of
|
||||
writing it back to go.mod. The JSON output corresponds to these Go types:
|
||||
|
||||
type Module struct {
|
||||
Path string
|
||||
Version string
|
||||
}
|
||||
|
||||
type GoMod struct {
|
||||
Module Module
|
||||
Require []Require
|
||||
Exclude []Module
|
||||
Replace []Replace
|
||||
}
|
||||
|
||||
type Require struct {
|
||||
Path string
|
||||
Version string
|
||||
Indirect bool
|
||||
}
|
||||
|
||||
type Replace struct {
|
||||
Old Module
|
||||
New Module
|
||||
}
|
||||
|
||||
Note that this only describes the go.mod file itself, not other modules
|
||||
referred to indirectly. For the full set of modules available to a build,
|
||||
use 'go list -m -json all'.
|
||||
|
||||
For example, a tool can obtain the go.mod as a data structure by
|
||||
parsing the output of 'go mod edit -json' and can then make changes
|
||||
by invoking 'go mod edit' with -require, -exclude, and so on.
|
||||
`,
|
||||
}
|
||||
|
||||
var (
|
||||
editFmt = cmdEdit.Flag.Bool("fmt", false, "")
|
||||
// editGo = cmdEdit.Flag.String("go", "", "")
|
||||
editJSON = cmdEdit.Flag.Bool("json", false, "")
|
||||
editPrint = cmdEdit.Flag.Bool("print", false, "")
|
||||
editModule = cmdEdit.Flag.String("module", "", "")
|
||||
edits []func(*modfile.File) // edits specified in flags
|
||||
)
|
||||
|
||||
type flagFunc func(string)
|
||||
|
||||
func (f flagFunc) String() string { return "" }
|
||||
func (f flagFunc) Set(s string) error { f(s); return nil }
|
||||
|
||||
func init() {
|
||||
cmdEdit.Run = runEdit // break init cycle
|
||||
|
||||
cmdEdit.Flag.Var(flagFunc(flagRequire), "require", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagDropRequire), "droprequire", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagExclude), "exclude", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagDropReplace), "dropreplace", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagReplace), "replace", "")
|
||||
cmdEdit.Flag.Var(flagFunc(flagDropExclude), "dropexclude", "")
|
||||
|
||||
base.AddBuildFlagsNX(&cmdEdit.Flag)
|
||||
}
|
||||
|
||||
func runEdit(cmd *base.Command, args []string) {
|
||||
anyFlags :=
|
||||
*editModule != "" ||
|
||||
*editJSON ||
|
||||
*editPrint ||
|
||||
*editFmt ||
|
||||
len(edits) > 0
|
||||
|
||||
if !anyFlags {
|
||||
base.Fatalf("go mod edit: no flags specified (see 'go help mod edit').")
|
||||
}
|
||||
|
||||
if *editJSON && *editPrint {
|
||||
base.Fatalf("go mod edit: cannot use both -json and -print")
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
base.Fatalf("go mod edit: too many arguments")
|
||||
}
|
||||
var gomod string
|
||||
if len(args) == 1 {
|
||||
gomod = args[0]
|
||||
} else {
|
||||
modload.MustInit()
|
||||
gomod = filepath.Join(modload.ModRoot, "go.mod")
|
||||
}
|
||||
|
||||
if *editModule != "" {
|
||||
if err := module.CheckPath(*editModule); err != nil {
|
||||
base.Fatalf("go mod: invalid -module: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(rsc): Implement -go= once we start advertising it.
|
||||
|
||||
data, err := ioutil.ReadFile(gomod)
|
||||
if err != nil {
|
||||
base.Fatalf("go: %v", err)
|
||||
}
|
||||
|
||||
modFile, err := modfile.Parse(gomod, data, nil)
|
||||
if err != nil {
|
||||
base.Fatalf("go: errors parsing %s:\n%s", base.ShortPath(gomod), err)
|
||||
}
|
||||
|
||||
if *editModule != "" {
|
||||
modFile.AddModuleStmt(modload.CmdModModule)
|
||||
}
|
||||
|
||||
if len(edits) > 0 {
|
||||
for _, edit := range edits {
|
||||
edit(modFile)
|
||||
}
|
||||
}
|
||||
modFile.SortBlocks()
|
||||
modFile.Cleanup() // clean file after edits
|
||||
|
||||
if *editJSON {
|
||||
editPrintJSON(modFile)
|
||||
return
|
||||
}
|
||||
|
||||
data, err = modFile.Format()
|
||||
if err != nil {
|
||||
base.Fatalf("go: %v", err)
|
||||
}
|
||||
|
||||
if *editPrint {
|
||||
os.Stdout.Write(data)
|
||||
return
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(gomod, data, 0666); err != nil {
|
||||
base.Fatalf("go: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// parsePathVersion parses -flag=arg expecting arg to be path@version.
|
||||
func parsePathVersion(flag, arg string) (path, version string) {
|
||||
i := strings.Index(arg, "@")
|
||||
if i < 0 {
|
||||
base.Fatalf("go mod: -%s=%s: need path@version", flag, arg)
|
||||
}
|
||||
path, version = strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:])
|
||||
if err := module.CheckPath(path); err != nil {
|
||||
base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err)
|
||||
}
|
||||
|
||||
// We don't call modfile.CheckPathVersion, because that insists
|
||||
// on versions being in semver form, but here we want to allow
|
||||
// versions like "master" or "1234abcdef", which the go command will resolve
|
||||
// the next time it runs (or during -fix).
|
||||
// Even so, we need to make sure the version is a valid token.
|
||||
if modfile.MustQuote(version) {
|
||||
base.Fatalf("go mod: -%s=%s: invalid version %q", flag, arg, version)
|
||||
}
|
||||
|
||||
return path, version
|
||||
}
|
||||
|
||||
// parsePath parses -flag=arg expecting arg to be path (not path@version).
|
||||
func parsePath(flag, arg string) (path string) {
|
||||
if strings.Contains(arg, "@") {
|
||||
base.Fatalf("go mod: -%s=%s: need just path, not path@version", flag, arg)
|
||||
}
|
||||
path = arg
|
||||
if err := module.CheckPath(path); err != nil {
|
||||
base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
// parsePathVersionOptional parses path[@version], using adj to
|
||||
// describe any errors.
|
||||
func parsePathVersionOptional(adj, arg string, allowDirPath bool) (path, version string, err error) {
|
||||
if i := strings.Index(arg, "@"); i < 0 {
|
||||
path = arg
|
||||
} else {
|
||||
path, version = strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:])
|
||||
}
|
||||
if err := module.CheckPath(path); err != nil {
|
||||
if !allowDirPath || !modfile.IsDirectoryPath(path) {
|
||||
return path, version, fmt.Errorf("invalid %s path: %v", adj, err)
|
||||
}
|
||||
}
|
||||
if path != arg && modfile.MustQuote(version) {
|
||||
return path, version, fmt.Errorf("invalid %s version: %q", adj, version)
|
||||
}
|
||||
return path, version, nil
|
||||
}
|
||||
|
||||
// flagRequire implements the -require flag.
|
||||
func flagRequire(arg string) {
|
||||
path, version := parsePathVersion("require", arg)
|
||||
edits = append(edits, func(f *modfile.File) {
|
||||
if err := f.AddRequire(path, version); err != nil {
|
||||
base.Fatalf("go mod: -require=%s: %v", arg, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// flagDropRequire implements the -droprequire flag.
|
||||
func flagDropRequire(arg string) {
|
||||
path := parsePath("droprequire", arg)
|
||||
edits = append(edits, func(f *modfile.File) {
|
||||
if err := f.DropRequire(path); err != nil {
|
||||
base.Fatalf("go mod: -droprequire=%s: %v", arg, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// flagExclude implements the -exclude flag.
|
||||
func flagExclude(arg string) {
|
||||
path, version := parsePathVersion("exclude", arg)
|
||||
edits = append(edits, func(f *modfile.File) {
|
||||
if err := f.AddExclude(path, version); err != nil {
|
||||
base.Fatalf("go mod: -exclude=%s: %v", arg, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// flagDropExclude implements the -dropexclude flag.
|
||||
func flagDropExclude(arg string) {
|
||||
path, version := parsePathVersion("dropexclude", arg)
|
||||
edits = append(edits, func(f *modfile.File) {
|
||||
if err := f.DropExclude(path, version); err != nil {
|
||||
base.Fatalf("go mod: -dropexclude=%s: %v", arg, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// flagReplace implements the -replace flag.
|
||||
func flagReplace(arg string) {
|
||||
var i int
|
||||
if i = strings.Index(arg, "="); i < 0 {
|
||||
base.Fatalf("go mod: -replace=%s: need old[@v]=new[@w] (missing =)", arg)
|
||||
}
|
||||
old, new := strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:])
|
||||
if strings.HasPrefix(new, ">") {
|
||||
base.Fatalf("go mod: -replace=%s: separator between old and new is =, not =>", arg)
|
||||
}
|
||||
oldPath, oldVersion, err := parsePathVersionOptional("old", old, false)
|
||||
if err != nil {
|
||||
base.Fatalf("go mod: -replace=%s: %v", arg, err)
|
||||
}
|
||||
newPath, newVersion, err := parsePathVersionOptional("new", new, true)
|
||||
if err != nil {
|
||||
base.Fatalf("go mod: -replace=%s: %v", arg, err)
|
||||
}
|
||||
if newPath == new && !modfile.IsDirectoryPath(new) {
|
||||
base.Fatalf("go mod: -replace=%s: unversioned new path must be local directory", arg)
|
||||
}
|
||||
|
||||
edits = append(edits, func(f *modfile.File) {
|
||||
if err := f.AddReplace(oldPath, oldVersion, newPath, newVersion); err != nil {
|
||||
base.Fatalf("go mod: -replace=%s: %v", arg, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// flagDropReplace implements the -dropreplace flag.
|
||||
func flagDropReplace(arg string) {
|
||||
path, version, err := parsePathVersionOptional("old", arg, true)
|
||||
if err != nil {
|
||||
base.Fatalf("go mod: -dropreplace=%s: %v", arg, err)
|
||||
}
|
||||
edits = append(edits, func(f *modfile.File) {
|
||||
if err := f.DropReplace(path, version); err != nil {
|
||||
base.Fatalf("go mod: -dropreplace=%s: %v", arg, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// fileJSON is the -json output data structure.
|
||||
type fileJSON struct {
|
||||
Module module.Version
|
||||
Require []requireJSON
|
||||
Exclude []module.Version
|
||||
Replace []replaceJSON
|
||||
}
|
||||
|
||||
type requireJSON struct {
|
||||
Path string
|
||||
Version string `json:",omitempty"`
|
||||
Indirect bool `json:",omitempty"`
|
||||
}
|
||||
|
||||
type replaceJSON struct {
|
||||
Old module.Version
|
||||
New module.Version
|
||||
}
|
||||
|
||||
// editPrintJSON prints the -json output.
|
||||
func editPrintJSON(modFile *modfile.File) {
|
||||
var f fileJSON
|
||||
f.Module = modFile.Module.Mod
|
||||
for _, r := range modFile.Require {
|
||||
f.Require = append(f.Require, requireJSON{Path: r.Mod.Path, Version: r.Mod.Version, Indirect: r.Indirect})
|
||||
}
|
||||
for _, x := range modFile.Exclude {
|
||||
f.Exclude = append(f.Exclude, x.Mod)
|
||||
}
|
||||
for _, r := range modFile.Replace {
|
||||
f.Replace = append(f.Replace, replaceJSON{r.Old, r.New})
|
||||
}
|
||||
data, err := json.MarshalIndent(&f, "", "\t")
|
||||
if err != nil {
|
||||
base.Fatalf("go: internal error: %v", err)
|
||||
}
|
||||
data = append(data, '\n')
|
||||
os.Stdout.Write(data)
|
||||
}
|
73
libgo/go/cmd/go/internal/modcmd/graph.go
Normal file
73
libgo/go/cmd/go/internal/modcmd/graph.go
Normal file
|
@ -0,0 +1,73 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// go mod graph
|
||||
|
||||
package modcmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/par"
|
||||
)
|
||||
|
||||
var cmdGraph = &base.Command{
|
||||
UsageLine: "go mod graph",
|
||||
Short: "print module requirement graph",
|
||||
Long: `
|
||||
Graph prints the module requirement graph (with replacements applied)
|
||||
in text form. Each line in the output has two space-separated fields: a module
|
||||
and one of its requirements. Each module is identified as a string of the form
|
||||
path@version, except for the main module, which has no @version suffix.
|
||||
`,
|
||||
Run: runGraph,
|
||||
}
|
||||
|
||||
func runGraph(cmd *base.Command, args []string) {
|
||||
if len(args) > 0 {
|
||||
base.Fatalf("go mod graph: graph takes no arguments")
|
||||
}
|
||||
modload.LoadBuildList()
|
||||
|
||||
reqs := modload.MinReqs()
|
||||
format := func(m module.Version) string {
|
||||
if m.Version == "" {
|
||||
return m.Path
|
||||
}
|
||||
return m.Path + "@" + m.Version
|
||||
}
|
||||
|
||||
// Note: using par.Work only to manage work queue.
|
||||
// No parallelism here, so no locking.
|
||||
var out []string
|
||||
var deps int // index in out where deps start
|
||||
var work par.Work
|
||||
work.Add(modload.Target)
|
||||
work.Do(1, func(item interface{}) {
|
||||
m := item.(module.Version)
|
||||
list, _ := reqs.Required(m)
|
||||
for _, r := range list {
|
||||
work.Add(r)
|
||||
out = append(out, format(m)+" "+format(r)+"\n")
|
||||
}
|
||||
if m == modload.Target {
|
||||
deps = len(out)
|
||||
}
|
||||
})
|
||||
|
||||
sort.Slice(out[deps:], func(i, j int) bool {
|
||||
return out[deps+i][0] < out[deps+j][0]
|
||||
})
|
||||
|
||||
w := bufio.NewWriter(os.Stdout)
|
||||
for _, line := range out {
|
||||
w.WriteString(line)
|
||||
}
|
||||
w.Flush()
|
||||
}
|
41
libgo/go/cmd/go/internal/modcmd/init.go
Normal file
41
libgo/go/cmd/go/internal/modcmd/init.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// go mod init
|
||||
|
||||
package modcmd
|
||||
|
||||
import (
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/modload"
|
||||
"os"
|
||||
)
|
||||
|
||||
var cmdInit = &base.Command{
|
||||
UsageLine: "go mod init [module]",
|
||||
Short: "initialize new module in current directory",
|
||||
Long: `
|
||||
Init initializes and writes a new go.mod to the current directory,
|
||||
in effect creating a new module rooted at the current directory.
|
||||
The file go.mod must not already exist.
|
||||
If possible, init will guess the module path from import comments
|
||||
(see 'go help importpath') or from version control configuration.
|
||||
To override this guess, supply the module path as an argument.
|
||||
`,
|
||||
Run: runInit,
|
||||
}
|
||||
|
||||
func runInit(cmd *base.Command, args []string) {
|
||||
modload.CmdModInit = true
|
||||
if len(args) > 1 {
|
||||
base.Fatalf("go mod init: too many arguments")
|
||||
}
|
||||
if len(args) == 1 {
|
||||
modload.CmdModModule = args[0]
|
||||
}
|
||||
if _, err := os.Stat("go.mod"); err == nil {
|
||||
base.Fatalf("go mod init: go.mod already exists")
|
||||
}
|
||||
modload.InitMod() // does all the hard work
|
||||
}
|
31
libgo/go/cmd/go/internal/modcmd/mod.go
Normal file
31
libgo/go/cmd/go/internal/modcmd/mod.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package modcmd implements the ``go mod'' command.
|
||||
package modcmd
|
||||
|
||||
import "cmd/go/internal/base"
|
||||
|
||||
var CmdMod = &base.Command{
|
||||
UsageLine: "go mod",
|
||||
Short: "module maintenance",
|
||||
Long: `Go mod provides access to operations on modules.
|
||||
|
||||
Note that support for modules is built into all the go commands,
|
||||
not just 'go mod'. For example, day-to-day adding, removing, upgrading,
|
||||
and downgrading of dependencies should be done using 'go get'.
|
||||
See 'go help modules' for an overview of module functionality.
|
||||
`,
|
||||
|
||||
Commands: []*base.Command{
|
||||
cmdDownload,
|
||||
cmdEdit,
|
||||
cmdGraph,
|
||||
cmdInit,
|
||||
cmdTidy,
|
||||
cmdVendor,
|
||||
cmdVerify,
|
||||
cmdWhy,
|
||||
},
|
||||
}
|
90
libgo/go/cmd/go/internal/modcmd/tidy.go
Normal file
90
libgo/go/cmd/go/internal/modcmd/tidy.go
Normal file
|
@ -0,0 +1,90 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// go mod tidy
|
||||
|
||||
package modcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
)
|
||||
|
||||
var cmdTidy = &base.Command{
|
||||
UsageLine: "go mod tidy [-v]",
|
||||
Short: "add missing and remove unused modules",
|
||||
Long: `
|
||||
Tidy makes sure go.mod matches the source code in the module.
|
||||
It adds any missing modules necessary to build the current module's
|
||||
packages and dependencies, and it removes unused modules that
|
||||
don't provide any relevant packages. It also adds any missing entries
|
||||
to go.sum and removes any unnecessary ones.
|
||||
|
||||
The -v flag causes tidy to print information about removed modules
|
||||
to standard error.
|
||||
`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmdTidy.Run = runTidy // break init cycle
|
||||
cmdTidy.Flag.BoolVar(&cfg.BuildV, "v", false, "")
|
||||
}
|
||||
|
||||
func runTidy(cmd *base.Command, args []string) {
|
||||
if len(args) > 0 {
|
||||
base.Fatalf("go mod tidy: no arguments allowed")
|
||||
}
|
||||
|
||||
// LoadALL adds missing modules.
|
||||
// Remove unused modules.
|
||||
used := make(map[module.Version]bool)
|
||||
for _, pkg := range modload.LoadALL() {
|
||||
used[modload.PackageModule(pkg)] = true
|
||||
}
|
||||
used[modload.Target] = true // note: LoadALL initializes Target
|
||||
|
||||
inGoMod := make(map[string]bool)
|
||||
for _, r := range modload.ModFile().Require {
|
||||
inGoMod[r.Mod.Path] = true
|
||||
}
|
||||
|
||||
var keep []module.Version
|
||||
for _, m := range modload.BuildList() {
|
||||
if used[m] {
|
||||
keep = append(keep, m)
|
||||
} else if cfg.BuildV && inGoMod[m.Path] {
|
||||
fmt.Fprintf(os.Stderr, "unused %s\n", m.Path)
|
||||
}
|
||||
}
|
||||
modload.SetBuildList(keep)
|
||||
modTidyGoSum() // updates memory copy; WriteGoMod on next line flushes it out
|
||||
modload.WriteGoMod()
|
||||
}
|
||||
|
||||
// modTidyGoSum resets the go.sum file content
|
||||
// to be exactly what's needed for the current go.mod.
|
||||
func modTidyGoSum() {
|
||||
// Assuming go.sum already has at least enough from the successful load,
|
||||
// we only have to tell modfetch what needs keeping.
|
||||
reqs := modload.Reqs()
|
||||
keep := make(map[module.Version]bool)
|
||||
var walk func(module.Version)
|
||||
walk = func(m module.Version) {
|
||||
keep[m] = true
|
||||
list, _ := reqs.Required(m)
|
||||
for _, r := range list {
|
||||
if !keep[r] {
|
||||
walk(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
walk(modload.Target)
|
||||
modfetch.TrimGoSum(keep)
|
||||
}
|
200
libgo/go/cmd/go/internal/modcmd/vendor.go
Normal file
200
libgo/go/cmd/go/internal/modcmd/vendor.go
Normal file
|
@ -0,0 +1,200 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package modcmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
)
|
||||
|
||||
var cmdVendor = &base.Command{
|
||||
UsageLine: "go mod vendor [-v]",
|
||||
Short: "make vendored copy of dependencies",
|
||||
Long: `
|
||||
Vendor resets the main module's vendor directory to include all packages
|
||||
needed to build and test all the main module's packages.
|
||||
It does not include test code for vendored packages.
|
||||
|
||||
The -v flag causes vendor to print the names of vendored
|
||||
modules and packages to standard error.
|
||||
`,
|
||||
Run: runVendor,
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmdVendor.Flag.BoolVar(&cfg.BuildV, "v", false, "")
|
||||
}
|
||||
|
||||
func runVendor(cmd *base.Command, args []string) {
|
||||
if len(args) != 0 {
|
||||
base.Fatalf("go mod vendor: vendor takes no arguments")
|
||||
}
|
||||
pkgs := modload.LoadVendor()
|
||||
|
||||
vdir := filepath.Join(modload.ModRoot, "vendor")
|
||||
if err := os.RemoveAll(vdir); err != nil {
|
||||
base.Fatalf("go vendor: %v", err)
|
||||
}
|
||||
|
||||
modpkgs := make(map[module.Version][]string)
|
||||
for _, pkg := range pkgs {
|
||||
m := modload.PackageModule(pkg)
|
||||
if m == modload.Target {
|
||||
continue
|
||||
}
|
||||
modpkgs[m] = append(modpkgs[m], pkg)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
for _, m := range modload.BuildList()[1:] {
|
||||
if pkgs := modpkgs[m]; len(pkgs) > 0 {
|
||||
repl := ""
|
||||
if r := modload.Replacement(m); r.Path != "" {
|
||||
repl = " => " + r.Path
|
||||
if r.Version != "" {
|
||||
repl += " " + r.Version
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(&buf, "# %s %s%s\n", m.Path, m.Version, repl)
|
||||
if cfg.BuildV {
|
||||
fmt.Fprintf(os.Stderr, "# %s %s%s\n", m.Path, m.Version, repl)
|
||||
}
|
||||
for _, pkg := range pkgs {
|
||||
fmt.Fprintf(&buf, "%s\n", pkg)
|
||||
if cfg.BuildV {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", pkg)
|
||||
}
|
||||
vendorPkg(vdir, pkg)
|
||||
}
|
||||
}
|
||||
}
|
||||
if buf.Len() == 0 {
|
||||
fmt.Fprintf(os.Stderr, "go: no dependencies to vendor\n")
|
||||
return
|
||||
}
|
||||
if err := ioutil.WriteFile(filepath.Join(vdir, "modules.txt"), buf.Bytes(), 0666); err != nil {
|
||||
base.Fatalf("go vendor: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func vendorPkg(vdir, pkg string) {
|
||||
realPath := modload.ImportMap(pkg)
|
||||
if realPath != pkg && modload.ImportMap(realPath) != "" {
|
||||
fmt.Fprintf(os.Stderr, "warning: %s imported as both %s and %s; making two copies.\n", realPath, realPath, pkg)
|
||||
}
|
||||
|
||||
dst := filepath.Join(vdir, pkg)
|
||||
src := modload.PackageDir(realPath)
|
||||
if src == "" {
|
||||
fmt.Fprintf(os.Stderr, "internal error: no pkg for %s -> %s\n", pkg, realPath)
|
||||
}
|
||||
copyDir(dst, src, matchNonTest)
|
||||
if m := modload.PackageModule(realPath); m.Path != "" {
|
||||
copyMetadata(m.Path, realPath, dst, src)
|
||||
}
|
||||
}
|
||||
|
||||
type metakey struct {
|
||||
modPath string
|
||||
dst string
|
||||
}
|
||||
|
||||
var copiedMetadata = make(map[metakey]bool)
|
||||
|
||||
// copyMetadata copies metadata files from parents of src to parents of dst,
|
||||
// stopping after processing the src parent for modPath.
|
||||
func copyMetadata(modPath, pkg, dst, src string) {
|
||||
for parent := 0; ; parent++ {
|
||||
if copiedMetadata[metakey{modPath, dst}] {
|
||||
break
|
||||
}
|
||||
copiedMetadata[metakey{modPath, dst}] = true
|
||||
if parent > 0 {
|
||||
copyDir(dst, src, matchMetadata)
|
||||
}
|
||||
if modPath == pkg {
|
||||
break
|
||||
}
|
||||
pkg = filepath.Dir(pkg)
|
||||
dst = filepath.Dir(dst)
|
||||
src = filepath.Dir(src)
|
||||
}
|
||||
}
|
||||
|
||||
// metaPrefixes is the list of metadata file prefixes.
|
||||
// Vendoring copies metadata files from parents of copied directories.
|
||||
// Note that this list could be arbitrarily extended, and it is longer
|
||||
// in other tools (such as godep or dep). By using this limited set of
|
||||
// prefixes and also insisting on capitalized file names, we are trying
|
||||
// to nudge people toward more agreement on the naming
|
||||
// and also trying to avoid false positives.
|
||||
var metaPrefixes = []string{
|
||||
"AUTHORS",
|
||||
"CONTRIBUTORS",
|
||||
"COPYLEFT",
|
||||
"COPYING",
|
||||
"COPYRIGHT",
|
||||
"LEGAL",
|
||||
"LICENSE",
|
||||
"NOTICE",
|
||||
"PATENTS",
|
||||
}
|
||||
|
||||
// matchMetadata reports whether info is a metadata file.
|
||||
func matchMetadata(info os.FileInfo) bool {
|
||||
name := info.Name()
|
||||
for _, p := range metaPrefixes {
|
||||
if strings.HasPrefix(name, p) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// matchNonTest reports whether info is any non-test file (including non-Go files).
|
||||
func matchNonTest(info os.FileInfo) bool {
|
||||
return !strings.HasSuffix(info.Name(), "_test.go")
|
||||
}
|
||||
|
||||
// copyDir copies all regular files satisfying match(info) from src to dst.
|
||||
func copyDir(dst, src string, match func(os.FileInfo) bool) {
|
||||
files, err := ioutil.ReadDir(src)
|
||||
if err != nil {
|
||||
base.Fatalf("go vendor: %v", err)
|
||||
}
|
||||
if err := os.MkdirAll(dst, 0777); err != nil {
|
||||
base.Fatalf("go vendor: %v", err)
|
||||
}
|
||||
for _, file := range files {
|
||||
if file.IsDir() || !file.Mode().IsRegular() || !match(file) {
|
||||
continue
|
||||
}
|
||||
r, err := os.Open(filepath.Join(src, file.Name()))
|
||||
if err != nil {
|
||||
base.Fatalf("go vendor: %v", err)
|
||||
}
|
||||
w, err := os.Create(filepath.Join(dst, file.Name()))
|
||||
if err != nil {
|
||||
base.Fatalf("go vendor: %v", err)
|
||||
}
|
||||
if _, err := io.Copy(w, r); err != nil {
|
||||
base.Fatalf("go vendor: %v", err)
|
||||
}
|
||||
r.Close()
|
||||
if err := w.Close(); err != nil {
|
||||
base.Fatalf("go vendor: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
96
libgo/go/cmd/go/internal/modcmd/verify.go
Normal file
96
libgo/go/cmd/go/internal/modcmd/verify.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package modcmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/dirhash"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
)
|
||||
|
||||
var cmdVerify = &base.Command{
|
||||
UsageLine: "go mod verify",
|
||||
Short: "verify dependencies have expected content",
|
||||
Long: `
|
||||
Verify checks that the dependencies of the current module,
|
||||
which are stored in a local downloaded source cache, have not been
|
||||
modified since being downloaded. If all the modules are unmodified,
|
||||
verify prints "all modules verified." Otherwise it reports which
|
||||
modules have been changed and causes 'go mod' to exit with a
|
||||
non-zero status.
|
||||
`,
|
||||
Run: runVerify,
|
||||
}
|
||||
|
||||
func runVerify(cmd *base.Command, args []string) {
|
||||
if len(args) != 0 {
|
||||
// NOTE(rsc): Could take a module pattern.
|
||||
base.Fatalf("go mod verify: verify takes no arguments")
|
||||
}
|
||||
ok := true
|
||||
for _, mod := range modload.LoadBuildList()[1:] {
|
||||
ok = verifyMod(mod) && ok
|
||||
}
|
||||
if ok {
|
||||
fmt.Printf("all modules verified\n")
|
||||
}
|
||||
}
|
||||
|
||||
func verifyMod(mod module.Version) bool {
|
||||
ok := true
|
||||
zip, zipErr := modfetch.CachePath(mod, "zip")
|
||||
if zipErr == nil {
|
||||
_, zipErr = os.Stat(zip)
|
||||
}
|
||||
dir, dirErr := modfetch.DownloadDir(mod)
|
||||
if dirErr == nil {
|
||||
_, dirErr = os.Stat(dir)
|
||||
}
|
||||
data, err := ioutil.ReadFile(zip + "hash")
|
||||
if err != nil {
|
||||
if zipErr != nil && os.IsNotExist(zipErr) && dirErr != nil && os.IsNotExist(dirErr) {
|
||||
// Nothing downloaded yet. Nothing to verify.
|
||||
return true
|
||||
}
|
||||
base.Errorf("%s %s: missing ziphash: %v", mod.Path, mod.Version, err)
|
||||
return false
|
||||
}
|
||||
h := string(bytes.TrimSpace(data))
|
||||
|
||||
if zipErr != nil && os.IsNotExist(zipErr) {
|
||||
// ok
|
||||
} else {
|
||||
hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash)
|
||||
if err != nil {
|
||||
base.Errorf("%s %s: %v", mod.Path, mod.Version, err)
|
||||
return false
|
||||
} else if hZ != h {
|
||||
base.Errorf("%s %s: zip has been modified (%v)", mod.Path, mod.Version, zip)
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
if dirErr != nil && os.IsNotExist(dirErr) {
|
||||
// ok
|
||||
} else {
|
||||
hD, err := dirhash.HashDir(dir, mod.Path+"@"+mod.Version, dirhash.DefaultHash)
|
||||
if err != nil {
|
||||
|
||||
base.Errorf("%s %s: %v", mod.Path, mod.Version, err)
|
||||
return false
|
||||
}
|
||||
if hD != h {
|
||||
base.Errorf("%s %s: dir has been modified (%v)", mod.Path, mod.Version, dir)
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
return ok
|
||||
}
|
121
libgo/go/cmd/go/internal/modcmd/why.go
Normal file
121
libgo/go/cmd/go/internal/modcmd/why.go
Normal file
|
@ -0,0 +1,121 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package modcmd
|
||||
|
||||
import (
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/modload"
|
||||
"cmd/go/internal/module"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var cmdWhy = &base.Command{
|
||||
UsageLine: "go mod why [-m] [-vendor] packages...",
|
||||
Short: "explain why packages or modules are needed",
|
||||
Long: `
|
||||
Why shows a shortest path in the import graph from the main module to
|
||||
each of the listed packages. If the -m flag is given, why treats the
|
||||
arguments as a list of modules and finds a path to any package in each
|
||||
of the modules.
|
||||
|
||||
By default, why queries the graph of packages matched by "go list all",
|
||||
which includes tests for reachable packages. The -vendor flag causes why
|
||||
to exclude tests of dependencies.
|
||||
|
||||
The output is a sequence of stanzas, one for each package or module
|
||||
name on the command line, separated by blank lines. Each stanza begins
|
||||
with a comment line "# package" or "# module" giving the target
|
||||
package or module. Subsequent lines give a path through the import
|
||||
graph, one package per line. If the package or module is not
|
||||
referenced from the main module, the stanza will display a single
|
||||
parenthesized note indicating that fact.
|
||||
|
||||
For example:
|
||||
|
||||
$ go mod why golang.org/x/text/language golang.org/x/text/encoding
|
||||
# golang.org/x/text/language
|
||||
rsc.io/quote
|
||||
rsc.io/sampler
|
||||
golang.org/x/text/language
|
||||
|
||||
# golang.org/x/text/encoding
|
||||
(main module does not need package golang.org/x/text/encoding)
|
||||
$
|
||||
`,
|
||||
}
|
||||
|
||||
var (
|
||||
whyM = cmdWhy.Flag.Bool("m", false, "")
|
||||
whyVendor = cmdWhy.Flag.Bool("vendor", false, "")
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmdWhy.Run = runWhy // break init cycle
|
||||
}
|
||||
|
||||
func runWhy(cmd *base.Command, args []string) {
|
||||
loadALL := modload.LoadALL
|
||||
if *whyVendor {
|
||||
loadALL = modload.LoadVendor
|
||||
}
|
||||
if *whyM {
|
||||
listU := false
|
||||
listVersions := false
|
||||
for _, arg := range args {
|
||||
if strings.Contains(arg, "@") {
|
||||
base.Fatalf("go mod why: module query not allowed")
|
||||
}
|
||||
}
|
||||
mods := modload.ListModules(args, listU, listVersions)
|
||||
byModule := make(map[module.Version][]string)
|
||||
for _, path := range loadALL() {
|
||||
m := modload.PackageModule(path)
|
||||
if m.Path != "" {
|
||||
byModule[m] = append(byModule[m], path)
|
||||
}
|
||||
}
|
||||
sep := ""
|
||||
for _, m := range mods {
|
||||
best := ""
|
||||
bestDepth := 1000000000
|
||||
for _, path := range byModule[module.Version{Path: m.Path, Version: m.Version}] {
|
||||
d := modload.WhyDepth(path)
|
||||
if d > 0 && d < bestDepth {
|
||||
best = path
|
||||
bestDepth = d
|
||||
}
|
||||
}
|
||||
why := modload.Why(best)
|
||||
if why == "" {
|
||||
vendoring := ""
|
||||
if *whyVendor {
|
||||
vendoring = " to vendor"
|
||||
}
|
||||
why = "(main module does not need" + vendoring + " module " + m.Path + ")\n"
|
||||
}
|
||||
fmt.Printf("%s# %s\n%s", sep, m.Path, why)
|
||||
sep = "\n"
|
||||
}
|
||||
} else {
|
||||
matches := modload.ImportPaths(args) // resolve to packages
|
||||
loadALL() // rebuild graph, from main module (not from named packages)
|
||||
sep := ""
|
||||
for _, m := range matches {
|
||||
for _, path := range m.Pkgs {
|
||||
why := modload.Why(path)
|
||||
if why == "" {
|
||||
vendoring := ""
|
||||
if *whyVendor {
|
||||
vendoring = " to vendor"
|
||||
}
|
||||
why = "(main module does not need" + vendoring + " package " + path + ")\n"
|
||||
}
|
||||
fmt.Printf("%s# %s\n%s", sep, path, why)
|
||||
sep = "\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
90
libgo/go/cmd/go/internal/modconv/convert.go
Normal file
90
libgo/go/cmd/go/internal/modconv/convert.go
Normal file
|
@ -0,0 +1,90 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package modconv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"cmd/go/internal/base"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/par"
|
||||
"cmd/go/internal/semver"
|
||||
)
|
||||
|
||||
// ConvertLegacyConfig converts legacy config to modfile.
|
||||
// The file argument is slash-delimited.
|
||||
func ConvertLegacyConfig(f *modfile.File, file string, data []byte) error {
|
||||
i := strings.LastIndex(file, "/")
|
||||
j := -2
|
||||
if i >= 0 {
|
||||
j = strings.LastIndex(file[:i], "/")
|
||||
}
|
||||
convert := Converters[file[i+1:]]
|
||||
if convert == nil && j != -2 {
|
||||
convert = Converters[file[j+1:]]
|
||||
}
|
||||
if convert == nil {
|
||||
return fmt.Errorf("unknown legacy config file %s", file)
|
||||
}
|
||||
mf, err := convert(file, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing %s: %v", file, err)
|
||||
}
|
||||
|
||||
// Convert requirements block, which may use raw SHA1 hashes as versions,
|
||||
// to valid semver requirement list, respecting major versions.
|
||||
var work par.Work
|
||||
for _, r := range mf.Require {
|
||||
m := r.Mod
|
||||
if m.Path == "" {
|
||||
continue
|
||||
}
|
||||
work.Add(r.Mod)
|
||||
}
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
need = make(map[string]string)
|
||||
)
|
||||
work.Do(10, func(item interface{}) {
|
||||
r := item.(module.Version)
|
||||
repo, info, err := modfetch.ImportRepoRev(r.Path, r.Version)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "go: converting %s: stat %s@%s: %v\n", base.ShortPath(file), r.Path, r.Version, err)
|
||||
return
|
||||
}
|
||||
mu.Lock()
|
||||
path := repo.ModulePath()
|
||||
// Don't use semver.Max here; need to preserve +incompatible suffix.
|
||||
if v, ok := need[path]; !ok || semver.Compare(v, info.Version) < 0 {
|
||||
need[path] = info.Version
|
||||
}
|
||||
mu.Unlock()
|
||||
})
|
||||
|
||||
var paths []string
|
||||
for path := range need {
|
||||
paths = append(paths, path)
|
||||
}
|
||||
sort.Strings(paths)
|
||||
for _, path := range paths {
|
||||
f.AddNewRequire(path, need[path], false)
|
||||
}
|
||||
|
||||
for _, r := range mf.Replace {
|
||||
err := f.AddReplace(r.Old.Path, r.Old.Version, r.New.Path, r.New.Version)
|
||||
if err != nil {
|
||||
return fmt.Errorf("add replace: %v", err)
|
||||
}
|
||||
}
|
||||
f.Cleanup()
|
||||
return nil
|
||||
}
|
186
libgo/go/cmd/go/internal/modconv/convert_test.go
Normal file
186
libgo/go/cmd/go/internal/modconv/convert_test.go
Normal file
|
@ -0,0 +1,186 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package modconv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"cmd/go/internal/cfg"
|
||||
"cmd/go/internal/modfetch"
|
||||
"cmd/go/internal/modfetch/codehost"
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/module"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(testMain(m))
|
||||
}
|
||||
|
||||
func testMain(m *testing.M) int {
|
||||
if _, err := exec.LookPath("git"); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "skipping because git binary not found")
|
||||
fmt.Println("PASS")
|
||||
return 0
|
||||
}
|
||||
|
||||
dir, err := ioutil.TempDir("", "modconv-test-")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
modfetch.PkgMod = filepath.Join(dir, "pkg/mod")
|
||||
codehost.WorkRoot = filepath.Join(dir, "codework")
|
||||
|
||||
return m.Run()
|
||||
}
|
||||
|
||||
func TestConvertLegacyConfig(t *testing.T) {
|
||||
testenv.MustHaveExternalNetwork(t)
|
||||
|
||||
if testing.Verbose() {
|
||||
old := cfg.BuildX
|
||||
defer func() {
|
||||
cfg.BuildX = old
|
||||
}()
|
||||
cfg.BuildX = true
|
||||
}
|
||||
|
||||
var tests = []struct {
|
||||
path string
|
||||
vers string
|
||||
gomod string
|
||||
}{
|
||||
/*
|
||||
Different versions of git seem to find or not find
|
||||
github.com/Masterminds/semver's a93e51b5a57e,
|
||||
which is an unmerged pull request.
|
||||
We'd rather not provide access to unmerged pull requests,
|
||||
so the line is removed from the golden file here,
|
||||
but some git commands still find it somehow.
|
||||
|
||||
{
|
||||
// Gopkg.lock parsing.
|
||||
"github.com/golang/dep", "v0.4.0",
|
||||
`module github.com/golang/dep
|
||||
|
||||
require (
|
||||
github.com/Masterminds/vcs v1.11.1
|
||||
github.com/armon/go-radix v0.0.0-20160115234725-4239b77079c7
|
||||
github.com/boltdb/bolt v1.3.1
|
||||
github.com/go-yaml/yaml v0.0.0-20170407172122-cd8b52f8269e
|
||||
github.com/golang/protobuf v0.0.0-20170901042739-5afd06f9d81a
|
||||
github.com/jmank88/nuts v0.3.0
|
||||
github.com/nightlyone/lockfile v0.0.0-20170707060451-e83dc5e7bba0
|
||||
github.com/pelletier/go-toml v0.0.0-20171218135716-b8b5e7696574
|
||||
github.com/pkg/errors v0.8.0
|
||||
github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353
|
||||
golang.org/x/net v0.0.0-20170828231752-66aacef3dd8a
|
||||
golang.org/x/sync v0.0.0-20170517211232-f52d1811a629
|
||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea
|
||||
)`,
|
||||
},
|
||||
*/
|
||||
|
||||
// TODO: https://github.com/docker/distribution uses vendor.conf
|
||||
|
||||
{
|
||||
// Godeps.json parsing.
|
||||
// TODO: Should v2.0.0 work here too?
|
||||
"github.com/docker/distribution", "v0.0.0-20150410205453-85de3967aa93",
|
||||
`module github.com/docker/distribution
|
||||
|
||||
require (
|
||||
github.com/AdRoll/goamz v0.0.0-20150130162828-d3664b76d905
|
||||
github.com/MSOpenTech/azure-sdk-for-go v0.0.0-20150323223030-d90753bcad2e
|
||||
github.com/Sirupsen/logrus v0.7.3
|
||||
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd
|
||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b
|
||||
github.com/bugsnag/panicwrap v0.0.0-20141110184334-e5f9854865b9
|
||||
github.com/codegangsta/cli v0.0.0-20150131031259-6086d7927ec3
|
||||
github.com/docker/docker v0.0.0-20150204013315-165ea5c158cf
|
||||
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1
|
||||
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7
|
||||
github.com/gorilla/context v0.0.0-20140604161150-14f550f51af5
|
||||
github.com/gorilla/handlers v0.0.0-20140825150757-0e84b7d810c1
|
||||
github.com/gorilla/mux v0.0.0-20140926153814-e444e69cbd2e
|
||||
github.com/jlhawn/go-crypto v0.0.0-20150401213827-cd738dde20f0
|
||||
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43
|
||||
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50
|
||||
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f
|
||||
golang.org/x/net v0.0.0-20150202051010-1dfe7915deaf
|
||||
gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789
|
||||
gopkg.in/yaml.v2 v2.0.0-20150116202057-bef53efd0c76
|
||||
)`,
|
||||
},
|
||||
|
||||
{
|
||||
// golang.org/issue/24585 - confusion about v2.0.0 tag in legacy non-v2 module
|
||||
"github.com/fishy/gcsbucket", "v0.0.0-20150410205453-618d60fe84e0",
|
||||
`module github.com/fishy/gcsbucket
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.18.0
|
||||
github.com/fishy/fsdb v0.0.0-20180217030800-5527ded01371
|
||||
github.com/golang/protobuf v1.0.0
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible
|
||||
golang.org/x/net v0.0.0-20180216171745-136a25c244d3
|
||||
golang.org/x/oauth2 v0.0.0-20180207181906-543e37812f10
|
||||
golang.org/x/text v0.0.0-20180208041248-4e4a3210bb54
|
||||
google.golang.org/api v0.0.0-20180217000815-c7a403bb5fe1
|
||||
google.golang.org/appengine v1.0.0
|
||||
google.golang.org/genproto v0.0.0-20180206005123-2b5a72b8730b
|
||||
google.golang.org/grpc v1.10.0
|
||||
)`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(strings.Replace(tt.path, "/", "_", -1)+"_"+tt.vers, func(t *testing.T) {
|
||||
f, err := modfile.Parse("golden", []byte(tt.gomod), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want, err := f.Format()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dir, err := modfetch.Download(module.Version{Path: tt.path, Version: tt.vers})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for name := range Converters {
|
||||
file := filepath.Join(dir, name)
|
||||
data, err := ioutil.ReadFile(file)
|
||||
if err == nil {
|
||||
f := new(modfile.File)
|
||||
f.AddModuleStmt(tt.path)
|
||||
if err := ConvertLegacyConfig(f, filepath.ToSlash(file), data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out, err := f.Format()
|
||||
if err != nil {
|
||||
t.Fatalf("format after conversion: %v", err)
|
||||
}
|
||||
if !bytes.Equal(out, want) {
|
||||
t.Fatalf("final go.mod:\n%s\n\nwant:\n%s", out, want)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Fatalf("no converter found for %s@%s", tt.path, tt.vers)
|
||||
})
|
||||
}
|
||||
}
|
74
libgo/go/cmd/go/internal/modconv/dep.go
Normal file
74
libgo/go/cmd/go/internal/modconv/dep.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package modconv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"cmd/go/internal/modfile"
|
||||
"cmd/go/internal/module"
|
||||
"cmd/go/internal/semver"
|
||||
)
|
||||
|
||||
func ParseGopkgLock(file string, data []byte) (*modfile.File, error) {
|
||||
mf := new(modfile.File)
|
||||
var list []module.Version
|
||||
var r *module.Version
|
||||
for lineno, line := range strings.Split(string(data), "\n") {
|
||||
lineno++
|
||||
if i := strings.Index(line, "#"); i >= 0 {
|
||||
line = line[:i]
|
||||
}
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "[[projects]]" {
|
||||
list = append(list, module.Version{})
|
||||
r = &list[len(list)-1]
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(line, "[") {
|
||||
r = nil
|
||||
continue
|
||||
}
|
||||
if r == nil {
|
||||
continue
|
||||
}
|
||||
i := strings.Index(line, "=")
|
||||
if i < 0 {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(line[:i])
|
||||
val := strings.TrimSpace(line[i+1:])
|
||||
if len(val) >= 2 && val[0] == '"' && val[len(val)-1] == '"' {
|
||||
q, err := strconv.Unquote(val) // Go unquoting, but close enough for now
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s:%d: invalid quoted string: %v", file, lineno, err)
|
||||
}
|
||||
val = q
|
||||
}
|
||||
switch key {
|
||||
case "name":
|
||||
r.Path = val
|
||||
case "revision", "version":
|
||||
// Note: key "version" should take priority over "revision",
|
||||
// and it does, because dep writes toml keys in alphabetical order,
|
||||
// so we see version (if present) second.
|
||||
if key == "version" {
|
||||
if !semver.IsValid(val) || semver.Canonical(val) != val {
|
||||
break
|
||||
}
|
||||
}
|
||||
r.Version = val
|
||||
}
|
||||
}
|
||||
for _, r := range list {
|
||||
if r.Path == "" || r.Version == "" {
|
||||
return nil, fmt.Errorf("%s: empty [[projects]] stanza (%s)", file, r.Path)
|
||||
}
|
||||
mf.Require = append(mf.Require, &modfile.Require{Mod: r})
|
||||
}
|
||||
return mf, nil
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue