libgo: Update to go1.6rc1.

Reviewed-on: https://go-review.googlesource.com/19200

From-SVN: r233110
This commit is contained in:
Ian Lance Taylor 2016-02-03 21:58:02 +00:00 committed by Ian Lance Taylor
parent b081ed4efc
commit f98dd1a338
728 changed files with 43810 additions and 11912 deletions

View File

@ -1,3 +1,9 @@
2016-02-03 Ian Lance Taylor <iant@google.com>
* Makefile.am (go_cmd_gofmt_files): Update to Go 1.6rc1 by adding
internal.go.
* Makefile.in: Rebuild.
2015-12-02 Ian Lance Taylor <iant@google.com>
PR go/66147

View File

@ -76,6 +76,7 @@ go_cmd_go_files = \
go_cmd_gofmt_files = \
$(cmdsrcdir)/gofmt/doc.go \
$(cmdsrcdir)/gofmt/gofmt.go \
$(cmdsrcdir)/gofmt/internal.go \
$(cmdsrcdir)/gofmt/rewrite.go \
$(cmdsrcdir)/gofmt/simplify.go

View File

@ -294,6 +294,7 @@ go_cmd_go_files = \
go_cmd_gofmt_files = \
$(cmdsrcdir)/gofmt/doc.go \
$(cmdsrcdir)/gofmt/gofmt.go \
$(cmdsrcdir)/gofmt/internal.go \
$(cmdsrcdir)/gofmt/rewrite.go \
$(cmdsrcdir)/gofmt/simplify.go
@ -563,8 +564,8 @@ distclean-generic:
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
@NATIVE_FALSE@install-exec-local:
@NATIVE_FALSE@uninstall-local:
@NATIVE_FALSE@install-exec-local:
clean: clean-am
clean-am: clean-binPROGRAMS clean-generic clean-noinstPROGRAMS \

View File

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

View File

@ -846,9 +846,7 @@ go_net_common_files = \
go/net/parse.go \
go/net/pipe.go \
go/net/fd_poll_runtime.go \
go/net/port.go \
go/net/port_unix.go \
go/net/race0.go \
$(go_net_sendfile_file) \
go/net/sock_posix.go \
$(go_net_sock_file) \
@ -1018,7 +1016,7 @@ go_os_files = \
$(go_os_sys_file) \
$(go_os_cloexec_file) \
go/os/types.go \
go/os/types_notwin.go
go/os/types_unix.go
go_path_files = \
go/path/match.go \
@ -1100,7 +1098,8 @@ go_strings_files = \
go/strings/replace.go \
go/strings/search.go \
go/strings/strings.go \
go/strings/strings_decl.go
go/strings/strings_decl.go \
go/strings/strings_generic.go
go_strings_c_files = \
go/strings/indexbyte.c
@ -1109,7 +1108,6 @@ go_sync_files = \
go/sync/mutex.go \
go/sync/once.go \
go/sync/pool.go \
go/sync/race0.go \
go/sync/runtime.go \
go/sync/rwmutex.go \
go/sync/waitgroup.go
@ -1196,7 +1194,6 @@ go_compress_bzip2_files = \
go_compress_flate_files = \
go/compress/flate/copy.go \
go/compress/flate/deflate.go \
go/compress/flate/fixedhuff.go \
go/compress/flate/huffman_bit_writer.go \
go/compress/flate/huffman_code.go \
go/compress/flate/inflate.go \
@ -1367,7 +1364,8 @@ go_debug_dwarf_files = \
go/debug/dwarf/unit.go
go_debug_elf_files = \
go/debug/elf/elf.go \
go/debug/elf/file.go
go/debug/elf/file.go \
go/debug/elf/reader.go
go_debug_gosym_files = \
go/debug/gosym/pclntab.go \
go/debug/gosym/symtab.go
@ -1450,7 +1448,6 @@ go_go_build_files = \
go/go/build/read.go \
go/go/build/syslist.go
go_go_constant_files = \
go/go/constant/go14.go \
go/go/constant/value.go
go_go_doc_files = \
go/go/doc/comment.go \
@ -1461,7 +1458,8 @@ go_go_doc_files = \
go/go/doc/reader.go \
go/go/doc/synopsis.go
go_go_format_files = \
go/go/format/format.go
go/go/format/format.go \
go/go/format/internal.go
go_go_importer_files = \
go/go/importer/importer.go
go_go_parser_files = \
@ -1489,7 +1487,6 @@ go_go_types_files = \
go/go/types/eval.go \
go/go/types/expr.go \
go/go/types/exprstring.go \
go/go/types/go12.go \
go/go/types/initorder.go \
go/go/types/labels.go \
go/go/types/lookup.go \
@ -1512,6 +1509,7 @@ go_go_types_files = \
go/go/types/universe.go
go_go_internal_gcimporter_files = \
go/go/internal/gcimporter/bimport.go \
go/go/internal/gcimporter/exportdata.go \
go/go/internal/gcimporter/gcimporter.go
go_go_internal_gccgoimporter_files = \
@ -1578,20 +1576,46 @@ go_index_suffixarray_files = \
go/index/suffixarray/qsufsort.go \
go/index/suffixarray/suffixarray.go
go_internal_format_files = \
go/internal/format/format.go
go_internal_golang_org_x_net_http2_hpack_files = \
go/internal/golang.org/x/net/http2/hpack/encode.go \
go/internal/golang.org/x/net/http2/hpack/hpack.go \
go/internal/golang.org/x/net/http2/hpack/huffman.go \
go/internal/golang.org/x/net/http2/hpack/tables.go
go_internal_race_files = \
go/internal/race/doc.go \
go/internal/race/norace.go
go_internal_singleflight_files = \
go/internal/singleflight/singleflight.go
if LIBGO_IS_LINUX
internal_syscall_unix_getrandom_file = go/internal/syscall/unix/getrandom_linux.go
if LIBGO_IS_386
internal_syscall_unix_getrandom_files = go/internal/syscall/unix/getrandom_linux.go go/internal/syscall/unix/getrandom_linux_386.go
else
internal_syscall_unix_getrandom_file =
if LIBGO_IS_X86_64
internal_syscall_unix_getrandom_files = go/internal/syscall/unix/getrandom_linux.go go/internal/syscall/unix/getrandom_linux_amd64.go
else
if LIBGO_IS_ARM
internal_syscall_unix_getrandom_files = go/internal/syscall/unix/getrandom_linux.go go/internal/syscall/unix/getrandom_linux_arm.go
else
if LIBGO_IS_PPC64
internal_syscall_unix_getrandom_files = go/internal/syscall/unix/getrandom_linux.go go/internal/syscall/unix/getrandom_linux_ppc64x.go
else
if LIBGO_IS_MIPS64
internal_syscall_unix_getrandom_files = go/internal/syscall/unix/getrandom_linux.go go/internal/syscall/unix/getrandom_linux_mips64x.go
else
internal_syscall_unix_getrandom_files = go/internal/syscall/unix/getrandom_linux.go go/internal/syscall/unix/getrandom_linux_generic.go
endif
endif
endif
endif
endif
else
internal_syscall_unix_getrandom_files =
endif
go_internal_syscall_unix_files = \
go/internal/syscall/unix/dummy.go \
$(internal_syscall_unix_getrandom_file)
$(internal_syscall_unix_getrandom_files)
go_internal_testenv_files = \
go/internal/testenv/testenv.go
@ -1608,15 +1632,19 @@ go_math_big_files = \
go/math/big/arith.go \
go/math/big/arith_decl_pure.go \
go/math/big/decimal.go \
go/math/big/doc.go \
go/math/big/float.go \
go/math/big/floatconv.go \
go/math/big/floatmarsh.go \
go/math/big/ftoa.go \
go/math/big/int.go \
go/math/big/intmarsh.go \
go/math/big/intconv.go \
go/math/big/nat.go \
go/math/big/natconv.go \
go/math/big/rat.go \
go/math/big/ratconv.go \
go/math/big/ratmarsh.go \
go/math/big/roundingmode_string.go
go_math_cmplx_files = \
go/math/cmplx/abs.go \
@ -1654,9 +1682,11 @@ go_net_http_files = \
go/net/http/cookie.go \
go/net/http/filetransport.go \
go/net/http/fs.go \
go/net/http/h2_bundle.go \
go/net/http/header.go \
go/net/http/jar.go \
go/net/http/lex.go \
go/net/http/method.go \
go/net/http/request.go \
go/net/http/response.go \
go/net/http/server.go \
@ -1698,7 +1728,8 @@ go_net_http_httputil_files = \
go/net/http/httputil/persist.go \
go/net/http/httputil/reverseproxy.go
go_net_http_internal_files = \
go/net/http/internal/chunked.go
go/net/http/internal/chunked.go \
go/net/http/internal/testcert.go
if LIBGO_IS_LINUX
go_net_internal_socktest_sys = go/net/internal/socktest/sys_cloexec.go
@ -1731,6 +1762,7 @@ go_os_exec_files = \
go/os/exec/lp_unix.go
go_os_signal_files = \
go/os/signal/doc.go \
go/os/signal/signal.go \
go/os/signal/signal_unix.go
@ -1999,7 +2031,7 @@ go_base_syscall_files = \
go/syscall/syscall_errno.go \
go/syscall/libcall_support.go \
go/syscall/libcall_posix.go \
go/syscall/race0.go \
go/syscall/msan0.go \
go/syscall/socket.go \
go/syscall/sockcmsg_unix.go \
go/syscall/str.go \
@ -2229,7 +2261,8 @@ libgo_go_objs = \
image/jpeg.lo \
image/png.lo \
index/suffixarray.lo \
internal/format.lo \
internal/golang.org/x/net/http2/hpack.lo \
internal/race.lo \
internal/singleflight.lo \
internal/syscall/unix.lo \
internal/testenv.lo \
@ -2358,7 +2391,7 @@ CHECK = \
elif test "$(GOBENCH)" != ""; then \
$(SHELL) $(srcdir)/testsuite/gotest --goarch=$(GOARCH) --goos=$(GOOS) --basedir=$(srcdir) --srcdir=$(srcdir)/go/$(@D) --pkgpath="$(@D)" --pkgfiles="$(go_$(subst /,_,$(@D))_files)" --bench="$(GOBENCH)" $(GOTESTFLAGS) $(go_$(subst /,_,$(@D))_test_files); \
else \
if $(SHELL) $(srcdir)/testsuite/gotest --goarch=$(GOARCH) --goos=$(GOOS) --basedir=$(srcdir) --srcdir=$(srcdir)/go/$(@D) --pkgpath="$(@D)" --pkgfiles="$(go_$(subst /,_,$(@D))_files)" $(GOTESTFLAGS) $(go_$(subst /,_,$(@D))_test_files) >>$@-testlog 2>&1; then \
if $(SHELL) $(srcdir)/testsuite/gotest --goarch=$(GOARCH) --goos=$(GOOS) --basedir=$(srcdir) --srcdir=$(srcdir)/go/$(@D) --pkgpath="$(@D)" --pkgfiles="$(go_$(subst .,_,$(subst /,_,$(@D)))_files)" $(GOTESTFLAGS) $(go_$(subst /,_,$(@D))_test_files) >>$@-testlog 2>&1; then \
echo "PASS: $(@D)" >> $@-testlog; \
echo "PASS: $(@D)"; \
echo "PASS: $(@D)" > $@-testsum; \
@ -3350,14 +3383,23 @@ index/suffixarray/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: index/suffixarray/check
@go_include@ internal/format.lo.dep
internal/format.lo.dep: $(go_internal_format_files)
@go_include@ internal/golang.org/x/net/http2/hpack.lo.dep
internal/golang.org/x/net/http2/hpack.lo.dep: $(go_internal_golang_org_x_net_http2_hpack_files)
$(BUILDDEPS)
internal/format.lo: $(go_internal_format_files)
internal/golang.org/x/net/http2/hpack.lo: $(go_internal_golang_org_x_net_http2_hpack_files)
$(BUILDPACKAGE)
internal/format/check: $(CHECK_DEPS)
internal/golang.org/x/net/http2/hpack/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: internal/format/check
.PHONY: internal/golang.org/x/net/http2/hpack/check
@go_include@ internal/race.lo.dep
internal/race.lo.dep: $(go_internal_race_files)
$(BUILDDEPS)
internal/race.lo: $(go_internal_race_files)
$(BUILDPACKAGE)
internal/race/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: internal/race/check
@go_include@ internal/singleflight.lo.dep
internal/singleflight.lo.dep: $(go_internal_singleflight_files)
@ -4013,7 +4055,9 @@ image/color/palette.gox: image/color/palette.lo
index/suffixarray.gox: index/suffixarray.lo
$(BUILDGOX)
internal/format.gox: internal/format.lo
internal/golang.org/x/net/http2/hpack.gox: internal/golang.org/x/net/http2/hpack.lo
$(BUILDGOX)
internal/race.gox: internal/race.lo
$(BUILDGOX)
internal/singleflight.gox: internal/singleflight.lo
$(BUILDGOX)
@ -4217,6 +4261,7 @@ TEST_PACKAGES = \
image/jpeg/check \
image/png/check \
index/suffixarray/check \
internal/golang.org/x/net/http2/hpack/check \
internal/singleflight/check \
internal/trace/check \
io/ioutil/check \

View File

@ -205,7 +205,8 @@ am__DEPENDENCIES_2 = bufio.lo bytes.lo bytes/index.lo crypto.lo \
net/http/httputil.lo net/http/internal.lo net/http/pprof.lo \
image/color.lo image/color/palette.lo image/draw.lo \
image/gif.lo image/internal/imageutil.lo image/jpeg.lo \
image/png.lo index/suffixarray.lo internal/format.lo \
image/png.lo index/suffixarray.lo \
internal/golang.org/x/net/http2/hpack.lo internal/race.lo \
internal/singleflight.lo internal/syscall/unix.lo \
internal/testenv.lo internal/trace.lo io/ioutil.lo \
log/syslog.lo log/syslog/syslog_c.lo math/big.lo math/cmplx.lo \
@ -1129,9 +1130,7 @@ go_net_common_files = \
go/net/parse.go \
go/net/pipe.go \
go/net/fd_poll_runtime.go \
go/net/port.go \
go/net/port_unix.go \
go/net/race0.go \
$(go_net_sendfile_file) \
go/net/sock_posix.go \
$(go_net_sock_file) \
@ -1216,7 +1215,7 @@ go_os_files = \
$(go_os_sys_file) \
$(go_os_cloexec_file) \
go/os/types.go \
go/os/types_notwin.go
go/os/types_unix.go
go_path_files = \
go/path/match.go \
@ -1274,7 +1273,8 @@ go_strings_files = \
go/strings/replace.go \
go/strings/search.go \
go/strings/strings.go \
go/strings/strings_decl.go
go/strings/strings_decl.go \
go/strings/strings_generic.go
go_strings_c_files = \
go/strings/indexbyte.c
@ -1284,7 +1284,6 @@ go_sync_files = \
go/sync/mutex.go \
go/sync/once.go \
go/sync/pool.go \
go/sync/race0.go \
go/sync/runtime.go \
go/sync/rwmutex.go \
go/sync/waitgroup.go
@ -1352,7 +1351,6 @@ go_compress_bzip2_files = \
go_compress_flate_files = \
go/compress/flate/copy.go \
go/compress/flate/deflate.go \
go/compress/flate/fixedhuff.go \
go/compress/flate/huffman_bit_writer.go \
go/compress/flate/huffman_code.go \
go/compress/flate/inflate.go \
@ -1511,7 +1509,8 @@ go_debug_dwarf_files = \
go_debug_elf_files = \
go/debug/elf/elf.go \
go/debug/elf/file.go
go/debug/elf/file.go \
go/debug/elf/reader.go
go_debug_gosym_files = \
go/debug/gosym/pclntab.go \
@ -1611,7 +1610,6 @@ go_go_build_files = \
go/go/build/syslist.go
go_go_constant_files = \
go/go/constant/go14.go \
go/go/constant/value.go
go_go_doc_files = \
@ -1624,7 +1622,8 @@ go_go_doc_files = \
go/go/doc/synopsis.go
go_go_format_files = \
go/go/format/format.go
go/go/format/format.go \
go/go/format/internal.go
go_go_importer_files = \
go/go/importer/importer.go
@ -1658,7 +1657,6 @@ go_go_types_files = \
go/go/types/eval.go \
go/go/types/expr.go \
go/go/types/exprstring.go \
go/go/types/go12.go \
go/go/types/initorder.go \
go/go/types/labels.go \
go/go/types/lookup.go \
@ -1681,6 +1679,7 @@ go_go_types_files = \
go/go/types/universe.go
go_go_internal_gcimporter_files = \
go/go/internal/gcimporter/bimport.go \
go/go/internal/gcimporter/exportdata.go \
go/go/internal/gcimporter/gcimporter.go
@ -1751,17 +1750,29 @@ go_index_suffixarray_files = \
go/index/suffixarray/qsufsort.go \
go/index/suffixarray/suffixarray.go
go_internal_format_files = \
go/internal/format/format.go
go_internal_golang_org_x_net_http2_hpack_files = \
go/internal/golang.org/x/net/http2/hpack/encode.go \
go/internal/golang.org/x/net/http2/hpack/hpack.go \
go/internal/golang.org/x/net/http2/hpack/huffman.go \
go/internal/golang.org/x/net/http2/hpack/tables.go
go_internal_race_files = \
go/internal/race/doc.go \
go/internal/race/norace.go
go_internal_singleflight_files = \
go/internal/singleflight/singleflight.go
@LIBGO_IS_LINUX_FALSE@internal_syscall_unix_getrandom_file =
@LIBGO_IS_LINUX_TRUE@internal_syscall_unix_getrandom_file = go/internal/syscall/unix/getrandom_linux.go
@LIBGO_IS_386_FALSE@@LIBGO_IS_ARM_FALSE@@LIBGO_IS_LINUX_TRUE@@LIBGO_IS_MIPS64_FALSE@@LIBGO_IS_PPC64_FALSE@@LIBGO_IS_X86_64_FALSE@internal_syscall_unix_getrandom_files = go/internal/syscall/unix/getrandom_linux.go go/internal/syscall/unix/getrandom_linux_generic.go
@LIBGO_IS_386_FALSE@@LIBGO_IS_ARM_FALSE@@LIBGO_IS_LINUX_TRUE@@LIBGO_IS_MIPS64_TRUE@@LIBGO_IS_PPC64_FALSE@@LIBGO_IS_X86_64_FALSE@internal_syscall_unix_getrandom_files = go/internal/syscall/unix/getrandom_linux.go go/internal/syscall/unix/getrandom_linux_mips64x.go
@LIBGO_IS_386_FALSE@@LIBGO_IS_ARM_FALSE@@LIBGO_IS_LINUX_TRUE@@LIBGO_IS_PPC64_TRUE@@LIBGO_IS_X86_64_FALSE@internal_syscall_unix_getrandom_files = go/internal/syscall/unix/getrandom_linux.go go/internal/syscall/unix/getrandom_linux_ppc64x.go
@LIBGO_IS_386_FALSE@@LIBGO_IS_ARM_TRUE@@LIBGO_IS_LINUX_TRUE@@LIBGO_IS_X86_64_FALSE@internal_syscall_unix_getrandom_files = go/internal/syscall/unix/getrandom_linux.go go/internal/syscall/unix/getrandom_linux_arm.go
@LIBGO_IS_386_FALSE@@LIBGO_IS_LINUX_TRUE@@LIBGO_IS_X86_64_TRUE@internal_syscall_unix_getrandom_files = go/internal/syscall/unix/getrandom_linux.go go/internal/syscall/unix/getrandom_linux_amd64.go
@LIBGO_IS_386_TRUE@@LIBGO_IS_LINUX_TRUE@internal_syscall_unix_getrandom_files = go/internal/syscall/unix/getrandom_linux.go go/internal/syscall/unix/getrandom_linux_386.go
@LIBGO_IS_LINUX_FALSE@internal_syscall_unix_getrandom_files =
go_internal_syscall_unix_files = \
go/internal/syscall/unix/dummy.go \
$(internal_syscall_unix_getrandom_file)
$(internal_syscall_unix_getrandom_files)
go_internal_testenv_files = \
go/internal/testenv/testenv.go
@ -1779,15 +1790,19 @@ go_math_big_files = \
go/math/big/arith.go \
go/math/big/arith_decl_pure.go \
go/math/big/decimal.go \
go/math/big/doc.go \
go/math/big/float.go \
go/math/big/floatconv.go \
go/math/big/floatmarsh.go \
go/math/big/ftoa.go \
go/math/big/int.go \
go/math/big/intmarsh.go \
go/math/big/intconv.go \
go/math/big/nat.go \
go/math/big/natconv.go \
go/math/big/rat.go \
go/math/big/ratconv.go \
go/math/big/ratmarsh.go \
go/math/big/roundingmode_string.go
go_math_cmplx_files = \
@ -1827,9 +1842,11 @@ go_net_http_files = \
go/net/http/cookie.go \
go/net/http/filetransport.go \
go/net/http/fs.go \
go/net/http/h2_bundle.go \
go/net/http/header.go \
go/net/http/jar.go \
go/net/http/lex.go \
go/net/http/method.go \
go/net/http/request.go \
go/net/http/response.go \
go/net/http/server.go \
@ -1881,7 +1898,8 @@ go_net_http_httputil_files = \
go/net/http/httputil/reverseproxy.go
go_net_http_internal_files = \
go/net/http/internal/chunked.go
go/net/http/internal/chunked.go \
go/net/http/internal/testcert.go
@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_LINUX_FALSE@go_net_internal_socktest_sys =
@LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_internal_socktest_sys = go/net/internal/socktest/sys_cloexec.go
@ -1908,6 +1926,7 @@ go_os_exec_files = \
go/os/exec/lp_unix.go
go_os_signal_files = \
go/os/signal/doc.go \
go/os/signal/signal.go \
go/os/signal/signal_unix.go
@ -2083,7 +2102,7 @@ go_base_syscall_files = \
go/syscall/syscall_errno.go \
go/syscall/libcall_support.go \
go/syscall/libcall_posix.go \
go/syscall/race0.go \
go/syscall/msan0.go \
go/syscall/socket.go \
go/syscall/sockcmsg_unix.go \
go/syscall/str.go \
@ -2253,7 +2272,8 @@ libgo_go_objs = \
image/jpeg.lo \
image/png.lo \
index/suffixarray.lo \
internal/format.lo \
internal/golang.org/x/net/http2/hpack.lo \
internal/race.lo \
internal/singleflight.lo \
internal/syscall/unix.lo \
internal/testenv.lo \
@ -2378,7 +2398,7 @@ CHECK = \
elif test "$(GOBENCH)" != ""; then \
$(SHELL) $(srcdir)/testsuite/gotest --goarch=$(GOARCH) --goos=$(GOOS) --basedir=$(srcdir) --srcdir=$(srcdir)/go/$(@D) --pkgpath="$(@D)" --pkgfiles="$(go_$(subst /,_,$(@D))_files)" --bench="$(GOBENCH)" $(GOTESTFLAGS) $(go_$(subst /,_,$(@D))_test_files); \
else \
if $(SHELL) $(srcdir)/testsuite/gotest --goarch=$(GOARCH) --goos=$(GOOS) --basedir=$(srcdir) --srcdir=$(srcdir)/go/$(@D) --pkgpath="$(@D)" --pkgfiles="$(go_$(subst /,_,$(@D))_files)" $(GOTESTFLAGS) $(go_$(subst /,_,$(@D))_test_files) >>$@-testlog 2>&1; then \
if $(SHELL) $(srcdir)/testsuite/gotest --goarch=$(GOARCH) --goos=$(GOOS) --basedir=$(srcdir) --srcdir=$(srcdir)/go/$(@D) --pkgpath="$(@D)" --pkgfiles="$(go_$(subst .,_,$(subst /,_,$(@D)))_files)" $(GOTESTFLAGS) $(go_$(subst /,_,$(@D))_test_files) >>$@-testlog 2>&1; then \
echo "PASS: $(@D)" >> $@-testlog; \
echo "PASS: $(@D)"; \
echo "PASS: $(@D)" > $@-testsum; \
@ -2511,6 +2531,7 @@ TEST_PACKAGES = \
image/jpeg/check \
image/png/check \
index/suffixarray/check \
internal/golang.org/x/net/http2/hpack/check \
internal/singleflight/check \
internal/trace/check \
io/ioutil/check \
@ -5743,14 +5764,23 @@ index/suffixarray/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: index/suffixarray/check
@go_include@ internal/format.lo.dep
internal/format.lo.dep: $(go_internal_format_files)
@go_include@ internal/golang.org/x/net/http2/hpack.lo.dep
internal/golang.org/x/net/http2/hpack.lo.dep: $(go_internal_golang_org_x_net_http2_hpack_files)
$(BUILDDEPS)
internal/format.lo: $(go_internal_format_files)
internal/golang.org/x/net/http2/hpack.lo: $(go_internal_golang_org_x_net_http2_hpack_files)
$(BUILDPACKAGE)
internal/format/check: $(CHECK_DEPS)
internal/golang.org/x/net/http2/hpack/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: internal/format/check
.PHONY: internal/golang.org/x/net/http2/hpack/check
@go_include@ internal/race.lo.dep
internal/race.lo.dep: $(go_internal_race_files)
$(BUILDDEPS)
internal/race.lo: $(go_internal_race_files)
$(BUILDPACKAGE)
internal/race/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: internal/race/check
@go_include@ internal/singleflight.lo.dep
internal/singleflight.lo.dep: $(go_internal_singleflight_files)
@ -6397,7 +6427,9 @@ image/color/palette.gox: image/color/palette.lo
index/suffixarray.gox: index/suffixarray.lo
$(BUILDGOX)
internal/format.gox: internal/format.lo
internal/golang.org/x/net/http2/hpack.gox: internal/golang.org/x/net/http2/hpack.lo
$(BUILDGOX)
internal/race.gox: internal/race.lo
$(BUILDGOX)
internal/singleflight.gox: internal/singleflight.lo
$(BUILDGOX)

View File

@ -1 +1 @@
go1.5.1
go1.6rc1

18
libgo/configure vendored
View File

@ -646,6 +646,8 @@ LIBGO_IS_PPC64_FALSE
LIBGO_IS_PPC64_TRUE
LIBGO_IS_PPC_FALSE
LIBGO_IS_PPC_TRUE
LIBGO_IS_MIPS64_FALSE
LIBGO_IS_MIPS64_TRUE
LIBGO_IS_MIPSO64_FALSE
LIBGO_IS_MIPSO64_TRUE
LIBGO_IS_MIPSN64_FALSE
@ -11124,7 +11126,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 11127 "configure"
#line 11129 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@ -11230,7 +11232,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 11233 "configure"
#line 11235 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@ -13884,6 +13886,14 @@ else
LIBGO_IS_MIPSO64_FALSE=
fi
if test $mips_abi = n64 -o $mips_abi = o64; then
LIBGO_IS_MIPS64_TRUE=
LIBGO_IS_MIPS64_FALSE='#'
else
LIBGO_IS_MIPS64_TRUE='#'
LIBGO_IS_MIPS64_FALSE=
fi
if test $is_ppc = yes; then
LIBGO_IS_PPC_TRUE=
LIBGO_IS_PPC_FALSE='#'
@ -15799,6 +15809,10 @@ if test -z "${LIBGO_IS_MIPSO64_TRUE}" && test -z "${LIBGO_IS_MIPSO64_FALSE}"; th
as_fn_error "conditional \"LIBGO_IS_MIPSO64\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
if test -z "${LIBGO_IS_MIPS64_TRUE}" && test -z "${LIBGO_IS_MIPS64_FALSE}"; then
as_fn_error "conditional \"LIBGO_IS_MIPS64\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
if test -z "${LIBGO_IS_PPC_TRUE}" && test -z "${LIBGO_IS_PPC_FALSE}"; then
as_fn_error "conditional \"LIBGO_IS_PPC\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5

View File

@ -316,6 +316,7 @@ AM_CONDITIONAL(LIBGO_IS_MIPSO32, test $mips_abi = o32)
AM_CONDITIONAL(LIBGO_IS_MIPSN32, test $mips_abi = n32)
AM_CONDITIONAL(LIBGO_IS_MIPSN64, test $mips_abi = n64)
AM_CONDITIONAL(LIBGO_IS_MIPSO64, test $mips_abi = o64)
AM_CONDITIONAL(LIBGO_IS_MIPS64, test $mips_abi = n64 -o $mips_abi = o64)
AM_CONDITIONAL(LIBGO_IS_PPC, test $is_ppc = yes)
AM_CONDITIONAL(LIBGO_IS_PPC64, test $is_ppc64 = yes)
AM_CONDITIONAL(LIBGO_IS_PPC64LE, test $is_ppc64le = yes)

View File

@ -327,3 +327,14 @@ func toASCII(s string) string {
}
return buf.String()
}
// isHeaderOnlyType checks if the given type flag is of the type that has no
// data section even if a size is specified.
func isHeaderOnlyType(flag byte) bool {
switch flag {
case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo:
return true
default:
return false
}
}

View File

@ -12,6 +12,7 @@ import (
"errors"
"io"
"io/ioutil"
"math"
"os"
"strconv"
"strings"
@ -36,6 +37,10 @@ type Reader struct {
hdrBuff [blockSize]byte // buffer to use in readHeader
}
type parser struct {
err error // Last error seen
}
// A numBytesReader is an io.Reader with a numBytes method, returning the number
// of bytes remaining in the underlying encoded data.
type numBytesReader interface {
@ -49,12 +54,36 @@ type regFileReader struct {
nb int64 // number of unread bytes for current file entry
}
// A sparseFileReader is a numBytesReader for reading sparse file data from a tar archive.
// A sparseFileReader is a numBytesReader for reading sparse file data from a
// tar archive.
type sparseFileReader struct {
rfr *regFileReader // reads the sparse-encoded file data
sp []sparseEntry // the sparse map for the file
pos int64 // keeps track of file position
tot int64 // total size of the file
rfr numBytesReader // Reads the sparse-encoded file data
sp []sparseEntry // The sparse map for the file
pos int64 // Keeps track of file position
total int64 // Total size of the file
}
// A sparseEntry holds a single entry in a sparse file's sparse map.
//
// Sparse files are represented using a series of sparseEntrys.
// Despite the name, a sparseEntry represents an actual data fragment that
// references data found in the underlying archive stream. All regions not
// covered by a sparseEntry are logically filled with zeros.
//
// For example, if the underlying raw file contains the 10-byte data:
// var compactData = "abcdefgh"
//
// And the sparse map has the following entries:
// var sp = []sparseEntry{
// {offset: 2, numBytes: 5} // Data fragment for [2..7]
// {offset: 18, numBytes: 3} // Data fragment for [18..21]
// }
//
// Then the content of the resulting sparse file with a "real" size of 25 is:
// var sparseData = "\x00"*2 + "abcde" + "\x00"*11 + "fgh" + "\x00"*4
type sparseEntry struct {
offset int64 // Starting position of the fragment
numBytes int64 // Length of the fragment
}
// Keywords for GNU sparse files in a PAX extended header
@ -88,69 +117,82 @@ func NewReader(r io.Reader) *Reader { return &Reader{r: r} }
//
// io.EOF is returned at the end of the input.
func (tr *Reader) Next() (*Header, error) {
var hdr *Header
if tr.err == nil {
tr.skipUnread()
}
if tr.err != nil {
return hdr, tr.err
return nil, tr.err
}
hdr = tr.readHeader()
if hdr == nil {
return hdr, tr.err
}
// Check for PAX/GNU header.
switch hdr.Typeflag {
case TypeXHeader:
// PAX extended header
headers, err := parsePAX(tr)
if err != nil {
return nil, err
}
// We actually read the whole file,
// but this skips alignment padding
tr.skipUnread()
var hdr *Header
var extHdrs map[string]string
// Externally, Next iterates through the tar archive as if it is a series of
// files. Internally, the tar format often uses fake "files" to add meta
// data that describes the next file. These meta data "files" should not
// normally be visible to the outside. As such, this loop iterates through
// one or more "header files" until it finds a "normal file".
loop:
for {
tr.err = tr.skipUnread()
if tr.err != nil {
return nil, tr.err
}
hdr = tr.readHeader()
if hdr == nil {
if tr.err != nil {
return nil, tr.err
}
mergePAX(hdr, headers)
// Check for a PAX format sparse file
sp, err := tr.checkForGNUSparsePAXHeaders(hdr, headers)
if err != nil {
tr.err = err
return nil, err
// Check for PAX/GNU special headers and files.
switch hdr.Typeflag {
case TypeXHeader:
extHdrs, tr.err = parsePAX(tr)
if tr.err != nil {
return nil, tr.err
}
continue loop // This is a meta header affecting the next header
case TypeGNULongName, TypeGNULongLink:
var realname []byte
realname, tr.err = ioutil.ReadAll(tr)
if tr.err != nil {
return nil, tr.err
}
// Convert GNU extensions to use PAX headers.
if extHdrs == nil {
extHdrs = make(map[string]string)
}
var p parser
switch hdr.Typeflag {
case TypeGNULongName:
extHdrs[paxPath] = p.parseString(realname)
case TypeGNULongLink:
extHdrs[paxLinkpath] = p.parseString(realname)
}
if p.err != nil {
tr.err = p.err
return nil, tr.err
}
continue loop // This is a meta header affecting the next header
default:
mergePAX(hdr, extHdrs)
// Check for a PAX format sparse file
sp, err := tr.checkForGNUSparsePAXHeaders(hdr, extHdrs)
if err != nil {
tr.err = err
return nil, err
}
if sp != nil {
// Current file is a PAX format GNU sparse file.
// Set the current file reader to a sparse file reader.
tr.curr, tr.err = newSparseFileReader(tr.curr, sp, hdr.Size)
if tr.err != nil {
return nil, tr.err
}
}
break loop // This is a file, so stop
}
if sp != nil {
// Current file is a PAX format GNU sparse file.
// Set the current file reader to a sparse file reader.
tr.curr = &sparseFileReader{rfr: tr.curr.(*regFileReader), sp: sp, tot: hdr.Size}
}
return hdr, nil
case TypeGNULongName:
// We have a GNU long name header. Its contents are the real file name.
realname, err := ioutil.ReadAll(tr)
if err != nil {
return nil, err
}
hdr, err := tr.Next()
hdr.Name = cString(realname)
return hdr, err
case TypeGNULongLink:
// We have a GNU long link header.
realname, err := ioutil.ReadAll(tr)
if err != nil {
return nil, err
}
hdr, err := tr.Next()
hdr.Linkname = cString(realname)
return hdr, err
}
return hdr, tr.err
return hdr, nil
}
// checkForGNUSparsePAXHeaders checks the PAX headers for GNU sparse headers. If they are found, then
@ -321,6 +363,7 @@ func parsePAX(r io.Reader) (map[string]string, error) {
if err != nil {
return nil, err
}
sbuf := string(buf)
// For GNU PAX sparse format 0.0 support.
// This function transforms the sparse format 0.0 headers into sparse format 0.1 headers.
@ -329,35 +372,17 @@ func parsePAX(r io.Reader) (map[string]string, error) {
headers := make(map[string]string)
// Each record is constructed as
// "%d %s=%s\n", length, keyword, value
for len(buf) > 0 {
// or the header was empty to start with.
var sp int
// The size field ends at the first space.
sp = bytes.IndexByte(buf, ' ')
if sp == -1 {
for len(sbuf) > 0 {
key, value, residual, err := parsePAXRecord(sbuf)
if err != nil {
return nil, ErrHeader
}
// Parse the first token as a decimal integer.
n, err := strconv.ParseInt(string(buf[:sp]), 10, 0)
if err != nil || n < 5 || int64(len(buf)) < n {
return nil, ErrHeader
}
// Extract everything between the decimal and the n -1 on the
// beginning to eat the ' ', -1 on the end to skip the newline.
var record []byte
record, buf = buf[sp+1:n-1], buf[n:]
// The first equals is guaranteed to mark the end of the key.
// Everything else is value.
eq := bytes.IndexByte(record, '=')
if eq == -1 {
return nil, ErrHeader
}
key, value := record[:eq], record[eq+1:]
sbuf = residual
keyStr := string(key)
if keyStr == paxGNUSparseOffset || keyStr == paxGNUSparseNumBytes {
// GNU sparse format 0.0 special key. Write to sparseMap instead of using the headers map.
sparseMap.Write(value)
sparseMap.WriteString(value)
sparseMap.Write([]byte{','})
} else {
// Normal key. Set the value in the headers map.
@ -372,9 +397,42 @@ func parsePAX(r io.Reader) (map[string]string, error) {
return headers, nil
}
// cString parses bytes as a NUL-terminated C-style string.
// parsePAXRecord parses the input PAX record string into a key-value pair.
// If parsing is successful, it will slice off the currently read record and
// return the remainder as r.
//
// A PAX record is of the following form:
// "%d %s=%s\n" % (size, key, value)
func parsePAXRecord(s string) (k, v, r string, err error) {
// The size field ends at the first space.
sp := strings.IndexByte(s, ' ')
if sp == -1 {
return "", "", s, ErrHeader
}
// Parse the first token as a decimal integer.
n, perr := strconv.ParseInt(s[:sp], 10, 0) // Intentionally parse as native int
if perr != nil || n < 5 || int64(len(s)) < n {
return "", "", s, ErrHeader
}
// Extract everything between the space and the final newline.
rec, nl, rem := s[sp+1:n-1], s[n-1:n], s[n:]
if nl != "\n" {
return "", "", s, ErrHeader
}
// The first equals separates the key from the value.
eq := strings.IndexByte(rec, '=')
if eq == -1 {
return "", "", s, ErrHeader
}
return rec[:eq], rec[eq+1:], rem, nil
}
// parseString parses bytes as a NUL-terminated C-style string.
// If a NUL byte is not found then the whole slice is returned as a string.
func cString(b []byte) string {
func (*parser) parseString(b []byte) string {
n := 0
for n < len(b) && b[n] != 0 {
n++
@ -382,19 +440,51 @@ func cString(b []byte) string {
return string(b[0:n])
}
func (tr *Reader) octal(b []byte) int64 {
// Check for binary format first.
// parseNumeric parses the input as being encoded in either base-256 or octal.
// This function may return negative numbers.
// If parsing fails or an integer overflow occurs, err will be set.
func (p *parser) parseNumeric(b []byte) int64 {
// Check for base-256 (binary) format first.
// If the first bit is set, then all following bits constitute a two's
// complement encoded number in big-endian byte order.
if len(b) > 0 && b[0]&0x80 != 0 {
var x int64
for i, c := range b {
if i == 0 {
c &= 0x7f // ignore signal bit in first byte
}
x = x<<8 | int64(c)
// Handling negative numbers relies on the following identity:
// -a-1 == ^a
//
// If the number is negative, we use an inversion mask to invert the
// data bytes and treat the value as an unsigned number.
var inv byte // 0x00 if positive or zero, 0xff if negative
if b[0]&0x40 != 0 {
inv = 0xff
}
return x
var x uint64
for i, c := range b {
c ^= inv // Inverts c only if inv is 0xff, otherwise does nothing
if i == 0 {
c &= 0x7f // Ignore signal bit in first byte
}
if (x >> 56) > 0 {
p.err = ErrHeader // Integer overflow
return 0
}
x = x<<8 | uint64(c)
}
if (x >> 63) > 0 {
p.err = ErrHeader // Integer overflow
return 0
}
if inv == 0xff {
return ^int64(x)
}
return int64(x)
}
// Normal case is base-8 (octal) format.
return p.parseOctal(b)
}
func (p *parser) parseOctal(b []byte) int64 {
// Because unused fields are filled with NULs, we need
// to skip leading NULs. Fields may also be padded with
// spaces or NULs.
@ -405,23 +495,52 @@ func (tr *Reader) octal(b []byte) int64 {
if len(b) == 0 {
return 0
}
x, err := strconv.ParseUint(cString(b), 8, 64)
if err != nil {
tr.err = err
x, perr := strconv.ParseUint(p.parseString(b), 8, 64)
if perr != nil {
p.err = ErrHeader
}
return int64(x)
}
// skipUnread skips any unread bytes in the existing file entry, as well as any alignment padding.
func (tr *Reader) skipUnread() {
nr := tr.numBytes() + tr.pad // number of bytes to skip
// skipUnread skips any unread bytes in the existing file entry, as well as any
// alignment padding. It returns io.ErrUnexpectedEOF if any io.EOF is
// encountered in the data portion; it is okay to hit io.EOF in the padding.
//
// Note that this function still works properly even when sparse files are being
// used since numBytes returns the bytes remaining in the underlying io.Reader.
func (tr *Reader) skipUnread() error {
dataSkip := tr.numBytes() // Number of data bytes to skip
totalSkip := dataSkip + tr.pad // Total number of bytes to skip
tr.curr, tr.pad = nil, 0
if sr, ok := tr.r.(io.Seeker); ok {
if _, err := sr.Seek(nr, os.SEEK_CUR); err == nil {
return
// If possible, Seek to the last byte before the end of the data section.
// Do this because Seek is often lazy about reporting errors; this will mask
// the fact that the tar stream may be truncated. We can rely on the
// io.CopyN done shortly afterwards to trigger any IO errors.
var seekSkipped int64 // Number of bytes skipped via Seek
if sr, ok := tr.r.(io.Seeker); ok && dataSkip > 1 {
// Not all io.Seeker can actually Seek. For example, os.Stdin implements
// io.Seeker, but calling Seek always returns an error and performs
// no action. Thus, we try an innocent seek to the current position
// to see if Seek is really supported.
pos1, err := sr.Seek(0, os.SEEK_CUR)
if err == nil {
// Seek seems supported, so perform the real Seek.
pos2, err := sr.Seek(dataSkip-1, os.SEEK_CUR)
if err != nil {
tr.err = err
return tr.err
}
seekSkipped = pos2 - pos1
}
}
_, tr.err = io.CopyN(ioutil.Discard, tr.r, nr)
var copySkipped int64 // Number of bytes skipped via CopyN
copySkipped, tr.err = io.CopyN(ioutil.Discard, tr.r, totalSkip-seekSkipped)
if tr.err == io.EOF && seekSkipped+copySkipped < dataSkip {
tr.err = io.ErrUnexpectedEOF
}
return tr.err
}
func (tr *Reader) verifyChecksum(header []byte) bool {
@ -429,23 +548,31 @@ func (tr *Reader) verifyChecksum(header []byte) bool {
return false
}
given := tr.octal(header[148:156])
var p parser
given := p.parseOctal(header[148:156])
unsigned, signed := checksum(header)
return given == unsigned || given == signed
return p.err == nil && (given == unsigned || given == signed)
}
// readHeader reads the next block header and assumes that the underlying reader
// is already aligned to a block boundary.
//
// The err will be set to io.EOF only when one of the following occurs:
// * Exactly 0 bytes are read and EOF is hit.
// * Exactly 1 block of zeros is read and EOF is hit.
// * At least 2 blocks of zeros are read.
func (tr *Reader) readHeader() *Header {
header := tr.hdrBuff[:]
copy(header, zeroBlock)
if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil {
return nil
return nil // io.EOF is okay here
}
// Two blocks of zero bytes marks the end of the archive.
if bytes.Equal(header, zeroBlock[0:blockSize]) {
if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil {
return nil
return nil // io.EOF is okay here
}
if bytes.Equal(header, zeroBlock[0:blockSize]) {
tr.err = io.EOF
@ -461,22 +588,19 @@ func (tr *Reader) readHeader() *Header {
}
// Unpack
var p parser
hdr := new(Header)
s := slicer(header)
hdr.Name = cString(s.next(100))
hdr.Mode = tr.octal(s.next(8))
hdr.Uid = int(tr.octal(s.next(8)))
hdr.Gid = int(tr.octal(s.next(8)))
hdr.Size = tr.octal(s.next(12))
if hdr.Size < 0 {
tr.err = ErrHeader
return nil
}
hdr.ModTime = time.Unix(tr.octal(s.next(12)), 0)
hdr.Name = p.parseString(s.next(100))
hdr.Mode = p.parseNumeric(s.next(8))
hdr.Uid = int(p.parseNumeric(s.next(8)))
hdr.Gid = int(p.parseNumeric(s.next(8)))
hdr.Size = p.parseNumeric(s.next(12))
hdr.ModTime = time.Unix(p.parseNumeric(s.next(12)), 0)
s.next(8) // chksum
hdr.Typeflag = s.next(1)[0]
hdr.Linkname = cString(s.next(100))
hdr.Linkname = p.parseString(s.next(100))
// The remainder of the header depends on the value of magic.
// The original (v7) version of tar had no explicit magic field,
@ -496,70 +620,76 @@ func (tr *Reader) readHeader() *Header {
switch format {
case "posix", "gnu", "star":
hdr.Uname = cString(s.next(32))
hdr.Gname = cString(s.next(32))
hdr.Uname = p.parseString(s.next(32))
hdr.Gname = p.parseString(s.next(32))
devmajor := s.next(8)
devminor := s.next(8)
if hdr.Typeflag == TypeChar || hdr.Typeflag == TypeBlock {
hdr.Devmajor = tr.octal(devmajor)
hdr.Devminor = tr.octal(devminor)
hdr.Devmajor = p.parseNumeric(devmajor)
hdr.Devminor = p.parseNumeric(devminor)
}
var prefix string
switch format {
case "posix", "gnu":
prefix = cString(s.next(155))
prefix = p.parseString(s.next(155))
case "star":
prefix = cString(s.next(131))
hdr.AccessTime = time.Unix(tr.octal(s.next(12)), 0)
hdr.ChangeTime = time.Unix(tr.octal(s.next(12)), 0)
prefix = p.parseString(s.next(131))
hdr.AccessTime = time.Unix(p.parseNumeric(s.next(12)), 0)
hdr.ChangeTime = time.Unix(p.parseNumeric(s.next(12)), 0)
}
if len(prefix) > 0 {
hdr.Name = prefix + "/" + hdr.Name
}
}
if tr.err != nil {
if p.err != nil {
tr.err = p.err
return nil
}
nb := hdr.Size
if isHeaderOnlyType(hdr.Typeflag) {
nb = 0
}
if nb < 0 {
tr.err = ErrHeader
return nil
}
// Maximum value of hdr.Size is 64 GB (12 octal digits),
// so there's no risk of int64 overflowing.
nb := int64(hdr.Size)
tr.pad = -nb & (blockSize - 1) // blockSize is a power of two
// Set the current file reader.
tr.pad = -nb & (blockSize - 1) // blockSize is a power of two
tr.curr = &regFileReader{r: tr.r, nb: nb}
// Check for old GNU sparse format entry.
if hdr.Typeflag == TypeGNUSparse {
// Get the real size of the file.
hdr.Size = tr.octal(header[483:495])
hdr.Size = p.parseNumeric(header[483:495])
if p.err != nil {
tr.err = p.err
return nil
}
// Read the sparse map.
sp := tr.readOldGNUSparseMap(header)
if tr.err != nil {
return nil
}
// Current file is a GNU sparse file. Update the current file reader.
tr.curr = &sparseFileReader{rfr: tr.curr.(*regFileReader), sp: sp, tot: hdr.Size}
tr.curr, tr.err = newSparseFileReader(tr.curr, sp, hdr.Size)
if tr.err != nil {
return nil
}
}
return hdr
}
// A sparseEntry holds a single entry in a sparse file's sparse map.
// A sparse entry indicates the offset and size in a sparse file of a
// block of data.
type sparseEntry struct {
offset int64
numBytes int64
}
// readOldGNUSparseMap reads the sparse map as stored in the old GNU sparse format.
// The sparse map is stored in the tar header if it's small enough. If it's larger than four entries,
// then one or more extension headers are used to store the rest of the sparse map.
func (tr *Reader) readOldGNUSparseMap(header []byte) []sparseEntry {
var p parser
isExtended := header[oldGNUSparseMainHeaderIsExtendedOffset] != 0
spCap := oldGNUSparseMainHeaderNumEntries
if isExtended {
@ -570,10 +700,10 @@ func (tr *Reader) readOldGNUSparseMap(header []byte) []sparseEntry {
// Read the four entries from the main tar header
for i := 0; i < oldGNUSparseMainHeaderNumEntries; i++ {
offset := tr.octal(s.next(oldGNUSparseOffsetSize))
numBytes := tr.octal(s.next(oldGNUSparseNumBytesSize))
if tr.err != nil {
tr.err = ErrHeader
offset := p.parseNumeric(s.next(oldGNUSparseOffsetSize))
numBytes := p.parseNumeric(s.next(oldGNUSparseNumBytesSize))
if p.err != nil {
tr.err = p.err
return nil
}
if offset == 0 && numBytes == 0 {
@ -591,10 +721,10 @@ func (tr *Reader) readOldGNUSparseMap(header []byte) []sparseEntry {
isExtended = sparseHeader[oldGNUSparseExtendedHeaderIsExtendedOffset] != 0
s = slicer(sparseHeader)
for i := 0; i < oldGNUSparseExtendedHeaderNumEntries; i++ {
offset := tr.octal(s.next(oldGNUSparseOffsetSize))
numBytes := tr.octal(s.next(oldGNUSparseNumBytesSize))
if tr.err != nil {
tr.err = ErrHeader
offset := p.parseNumeric(s.next(oldGNUSparseOffsetSize))
numBytes := p.parseNumeric(s.next(oldGNUSparseNumBytesSize))
if p.err != nil {
tr.err = p.err
return nil
}
if offset == 0 && numBytes == 0 {
@ -606,122 +736,111 @@ func (tr *Reader) readOldGNUSparseMap(header []byte) []sparseEntry {
return sp
}
// readGNUSparseMap1x0 reads the sparse map as stored in GNU's PAX sparse format version 1.0.
// The sparse map is stored just before the file data and padded out to the nearest block boundary.
// readGNUSparseMap1x0 reads the sparse map as stored in GNU's PAX sparse format
// version 1.0. The format of the sparse map consists of a series of
// newline-terminated numeric fields. The first field is the number of entries
// and is always present. Following this are the entries, consisting of two
// fields (offset, numBytes). This function must stop reading at the end
// boundary of the block containing the last newline.
//
// Note that the GNU manual says that numeric values should be encoded in octal
// format. However, the GNU tar utility itself outputs these values in decimal.
// As such, this library treats values as being encoded in decimal.
func readGNUSparseMap1x0(r io.Reader) ([]sparseEntry, error) {
buf := make([]byte, 2*blockSize)
sparseHeader := buf[:blockSize]
var cntNewline int64
var buf bytes.Buffer
var blk = make([]byte, blockSize)
// readDecimal is a helper function to read a decimal integer from the sparse map
// while making sure to read from the file in blocks of size blockSize
readDecimal := func() (int64, error) {
// Look for newline
nl := bytes.IndexByte(sparseHeader, '\n')
if nl == -1 {
if len(sparseHeader) >= blockSize {
// This is an error
return 0, ErrHeader
// feedTokens copies data in numBlock chunks from r into buf until there are
// at least cnt newlines in buf. It will not read more blocks than needed.
var feedTokens = func(cnt int64) error {
for cntNewline < cnt {
if _, err := io.ReadFull(r, blk); err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return err
}
oldLen := len(sparseHeader)
newLen := oldLen + blockSize
if cap(sparseHeader) < newLen {
// There's more header, but we need to make room for the next block
copy(buf, sparseHeader)
sparseHeader = buf[:newLen]
} else {
// There's more header, and we can just reslice
sparseHeader = sparseHeader[:newLen]
buf.Write(blk)
for _, c := range blk {
if c == '\n' {
cntNewline++
}
}
// Now that sparseHeader is large enough, read next block
if _, err := io.ReadFull(r, sparseHeader[oldLen:newLen]); err != nil {
return 0, err
}
// Look for a newline in the new data
nl = bytes.IndexByte(sparseHeader[oldLen:newLen], '\n')
if nl == -1 {
// This is an error
return 0, ErrHeader
}
nl += oldLen // We want the position from the beginning
}
// Now that we've found a newline, read a number
n, err := strconv.ParseInt(string(sparseHeader[:nl]), 10, 0)
if err != nil {
return 0, ErrHeader
}
// Update sparseHeader to consume this number
sparseHeader = sparseHeader[nl+1:]
return n, nil
return nil
}
// Read the first block
if _, err := io.ReadFull(r, sparseHeader); err != nil {
// nextToken gets the next token delimited by a newline. This assumes that
// at least one newline exists in the buffer.
var nextToken = func() string {
cntNewline--
tok, _ := buf.ReadString('\n')
return tok[:len(tok)-1] // Cut off newline
}
// Parse for the number of entries.
// Use integer overflow resistant math to check this.
if err := feedTokens(1); err != nil {
return nil, err
}
// The first line contains the number of entries
numEntries, err := readDecimal()
if err != nil {
return nil, err
numEntries, err := strconv.ParseInt(nextToken(), 10, 0) // Intentionally parse as native int
if err != nil || numEntries < 0 || int(2*numEntries) < int(numEntries) {
return nil, ErrHeader
}
// Read all the entries
// Parse for all member entries.
// numEntries is trusted after this since a potential attacker must have
// committed resources proportional to what this library used.
if err := feedTokens(2 * numEntries); err != nil {
return nil, err
}
sp := make([]sparseEntry, 0, numEntries)
for i := int64(0); i < numEntries; i++ {
// Read the offset
offset, err := readDecimal()
offset, err := strconv.ParseInt(nextToken(), 10, 64)
if err != nil {
return nil, err
return nil, ErrHeader
}
// Read numBytes
numBytes, err := readDecimal()
numBytes, err := strconv.ParseInt(nextToken(), 10, 64)
if err != nil {
return nil, err
return nil, ErrHeader
}
sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
}
return sp, nil
}
// readGNUSparseMap0x1 reads the sparse map as stored in GNU's PAX sparse format version 0.1.
// The sparse map is stored in the PAX headers.
func readGNUSparseMap0x1(headers map[string]string) ([]sparseEntry, error) {
// Get number of entries
numEntriesStr, ok := headers[paxGNUSparseNumBlocks]
if !ok {
return nil, ErrHeader
}
numEntries, err := strconv.ParseInt(numEntriesStr, 10, 0)
if err != nil {
// readGNUSparseMap0x1 reads the sparse map as stored in GNU's PAX sparse format
// version 0.1. The sparse map is stored in the PAX headers.
func readGNUSparseMap0x1(extHdrs map[string]string) ([]sparseEntry, error) {
// Get number of entries.
// Use integer overflow resistant math to check this.
numEntriesStr := extHdrs[paxGNUSparseNumBlocks]
numEntries, err := strconv.ParseInt(numEntriesStr, 10, 0) // Intentionally parse as native int
if err != nil || numEntries < 0 || int(2*numEntries) < int(numEntries) {
return nil, ErrHeader
}
sparseMap := strings.Split(headers[paxGNUSparseMap], ",")
// There should be two numbers in sparseMap for each entry
// There should be two numbers in sparseMap for each entry.
sparseMap := strings.Split(extHdrs[paxGNUSparseMap], ",")
if int64(len(sparseMap)) != 2*numEntries {
return nil, ErrHeader
}
// Loop through the entries in the sparse map
// Loop through the entries in the sparse map.
// numEntries is trusted now.
sp := make([]sparseEntry, 0, numEntries)
for i := int64(0); i < numEntries; i++ {
offset, err := strconv.ParseInt(sparseMap[2*i], 10, 0)
offset, err := strconv.ParseInt(sparseMap[2*i], 10, 64)
if err != nil {
return nil, ErrHeader
}
numBytes, err := strconv.ParseInt(sparseMap[2*i+1], 10, 0)
numBytes, err := strconv.ParseInt(sparseMap[2*i+1], 10, 64)
if err != nil {
return nil, ErrHeader
}
sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
}
return sp, nil
}
@ -738,10 +857,18 @@ func (tr *Reader) numBytes() int64 {
// Read reads from the current entry in the tar archive.
// It returns 0, io.EOF when it reaches the end of that entry,
// until Next is called to advance to the next entry.
//
// Calling Read on special types like TypeLink, TypeSymLink, TypeChar,
// TypeBlock, TypeDir, and TypeFifo returns 0, io.EOF regardless of what
// the Header.Size claims.
func (tr *Reader) Read(b []byte) (n int, err error) {
if tr.err != nil {
return 0, tr.err
}
if tr.curr == nil {
return 0, io.EOF
}
n, err = tr.curr.Read(b)
if err != nil && err != io.EOF {
tr.err = err
@ -771,9 +898,33 @@ func (rfr *regFileReader) numBytes() int64 {
return rfr.nb
}
// readHole reads a sparse file hole ending at offset toOffset
func (sfr *sparseFileReader) readHole(b []byte, toOffset int64) int {
n64 := toOffset - sfr.pos
// newSparseFileReader creates a new sparseFileReader, but validates all of the
// sparse entries before doing so.
func newSparseFileReader(rfr numBytesReader, sp []sparseEntry, total int64) (*sparseFileReader, error) {
if total < 0 {
return nil, ErrHeader // Total size cannot be negative
}
// Validate all sparse entries. These are the same checks as performed by
// the BSD tar utility.
for i, s := range sp {
switch {
case s.offset < 0 || s.numBytes < 0:
return nil, ErrHeader // Negative values are never okay
case s.offset > math.MaxInt64-s.numBytes:
return nil, ErrHeader // Integer overflow with large length
case s.offset+s.numBytes > total:
return nil, ErrHeader // Region extends beyond the "real" size
case i > 0 && sp[i-1].offset+sp[i-1].numBytes > s.offset:
return nil, ErrHeader // Regions can't overlap and must be in order
}
}
return &sparseFileReader{rfr: rfr, sp: sp, total: total}, nil
}
// readHole reads a sparse hole ending at endOffset.
func (sfr *sparseFileReader) readHole(b []byte, endOffset int64) int {
n64 := endOffset - sfr.pos
if n64 > int64(len(b)) {
n64 = int64(len(b))
}
@ -787,49 +938,54 @@ func (sfr *sparseFileReader) readHole(b []byte, toOffset int64) int {
// Read reads the sparse file data in expanded form.
func (sfr *sparseFileReader) Read(b []byte) (n int, err error) {
if len(sfr.sp) == 0 {
// No more data fragments to read from.
if sfr.pos < sfr.tot {
// We're in the last hole
n = sfr.readHole(b, sfr.tot)
return
}
// Otherwise, we're at the end of the file
return 0, io.EOF
}
if sfr.tot < sfr.sp[0].offset {
return 0, io.ErrUnexpectedEOF
}
if sfr.pos < sfr.sp[0].offset {
// We're in a hole
n = sfr.readHole(b, sfr.sp[0].offset)
return
// Skip past all empty fragments.
for len(sfr.sp) > 0 && sfr.sp[0].numBytes == 0 {
sfr.sp = sfr.sp[1:]
}
// We're not in a hole, so we'll read from the next data fragment
posInFragment := sfr.pos - sfr.sp[0].offset
bytesLeft := sfr.sp[0].numBytes - posInFragment
// If there are no more fragments, then it is possible that there
// is one last sparse hole.
if len(sfr.sp) == 0 {
// This behavior matches the BSD tar utility.
// However, GNU tar stops returning data even if sfr.total is unmet.
if sfr.pos < sfr.total {
return sfr.readHole(b, sfr.total), nil
}
return 0, io.EOF
}
// In front of a data fragment, so read a hole.
if sfr.pos < sfr.sp[0].offset {
return sfr.readHole(b, sfr.sp[0].offset), nil
}
// In a data fragment, so read from it.
// This math is overflow free since we verify that offset and numBytes can
// be safely added when creating the sparseFileReader.
endPos := sfr.sp[0].offset + sfr.sp[0].numBytes // End offset of fragment
bytesLeft := endPos - sfr.pos // Bytes left in fragment
if int64(len(b)) > bytesLeft {
b = b[0:bytesLeft]
b = b[:bytesLeft]
}
n, err = sfr.rfr.Read(b)
sfr.pos += int64(n)
if int64(n) == bytesLeft {
// We're done with this fragment
sfr.sp = sfr.sp[1:]
if err == io.EOF {
if sfr.pos < endPos {
err = io.ErrUnexpectedEOF // There was supposed to be more data
} else if sfr.pos < sfr.total {
err = nil // There is still an implicit sparse hole at the end
}
}
if err == io.EOF && sfr.pos < sfr.tot {
// We reached the end of the last fragment's data, but there's a final hole
err = nil
if sfr.pos == endPos {
sfr.sp = sfr.sp[1:] // We are done with this fragment, so pop it
}
return
return n, err
}
// numBytes returns the number of bytes left to read in the sparse file's
// sparse-encoded data in the tar archive.
func (sfr *sparseFileReader) numBytes() int64 {
return sfr.rfr.nb
return sfr.rfr.numBytes()
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -12,8 +12,8 @@ import (
"errors"
"fmt"
"io"
"os"
"path"
"sort"
"strconv"
"strings"
"time"
@ -23,7 +23,6 @@ var (
ErrWriteTooLong = errors.New("archive/tar: write too long")
ErrFieldTooLong = errors.New("archive/tar: header field too long")
ErrWriteAfterClose = errors.New("archive/tar: write after close")
errNameTooLong = errors.New("archive/tar: name too long")
errInvalidHeader = errors.New("archive/tar: header field too long or contains invalid values")
)
@ -43,6 +42,10 @@ type Writer struct {
paxHdrBuff [blockSize]byte // buffer to use in writeHeader when writing a pax header
}
type formatter struct {
err error // Last error seen
}
// NewWriter creates a new Writer writing to w.
func NewWriter(w io.Writer) *Writer { return &Writer{w: w} }
@ -69,17 +72,9 @@ func (tw *Writer) Flush() error {
}
// Write s into b, terminating it with a NUL if there is room.
// If the value is too long for the field and allowPax is true add a paxheader record instead
func (tw *Writer) cString(b []byte, s string, allowPax bool, paxKeyword string, paxHeaders map[string]string) {
needsPaxHeader := allowPax && len(s) > len(b) || !isASCII(s)
if needsPaxHeader {
paxHeaders[paxKeyword] = s
return
}
func (f *formatter) formatString(b []byte, s string) {
if len(s) > len(b) {
if tw.err == nil {
tw.err = ErrFieldTooLong
}
f.err = ErrFieldTooLong
return
}
ascii := toASCII(s)
@ -90,40 +85,40 @@ func (tw *Writer) cString(b []byte, s string, allowPax bool, paxKeyword string,
}
// Encode x as an octal ASCII string and write it into b with leading zeros.
func (tw *Writer) octal(b []byte, x int64) {
func (f *formatter) formatOctal(b []byte, x int64) {
s := strconv.FormatInt(x, 8)
// leading zeros, but leave room for a NUL.
for len(s)+1 < len(b) {
s = "0" + s
}
tw.cString(b, s, false, paxNone, nil)
f.formatString(b, s)
}
// Write x into b, either as octal or as binary (GNUtar/star extension).
// If the value is too long for the field and writingPax is enabled both for the field and the add a paxheader record instead
func (tw *Writer) numeric(b []byte, x int64, allowPax bool, paxKeyword string, paxHeaders map[string]string) {
// Try octal first.
s := strconv.FormatInt(x, 8)
if len(s) < len(b) {
tw.octal(b, x)
// fitsInBase256 reports whether x can be encoded into n bytes using base-256
// encoding. Unlike octal encoding, base-256 encoding does not require that the
// string ends with a NUL character. Thus, all n bytes are available for output.
//
// If operating in binary mode, this assumes strict GNU binary mode; which means
// that the first byte can only be either 0x80 or 0xff. Thus, the first byte is
// equivalent to the sign bit in two's complement form.
func fitsInBase256(n int, x int64) bool {
var binBits = uint(n-1) * 8
return n >= 9 || (x >= -1<<binBits && x < 1<<binBits)
}
// Write x into b, as binary (GNUtar/star extension).
func (f *formatter) formatNumeric(b []byte, x int64) {
if fitsInBase256(len(b), x) {
for i := len(b) - 1; i >= 0; i-- {
b[i] = byte(x)
x >>= 8
}
b[0] |= 0x80 // Highest bit indicates binary format
return
}
// If it is too long for octal, and pax is preferred, use a pax header
if allowPax && tw.preferPax {
tw.octal(b, 0)
s := strconv.FormatInt(x, 10)
paxHeaders[paxKeyword] = s
return
}
// Too big: use binary (big-endian).
tw.usedBinary = true
for i := len(b) - 1; x > 0 && i >= 0; i-- {
b[i] = byte(x)
x >>= 8
}
b[0] |= 0x80 // highest bit indicates binary format
f.formatOctal(b, 0) // Last resort, just write zero
f.err = ErrFieldTooLong
}
var (
@ -162,6 +157,7 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
// subsecond time resolution, but for now let's just capture
// too long fields or non ascii characters
var f formatter
var header []byte
// We need to select which scratch buffer to use carefully,
@ -176,10 +172,40 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
copy(header, zeroBlock)
s := slicer(header)
// Wrappers around formatter that automatically sets paxHeaders if the
// argument extends beyond the capacity of the input byte slice.
var formatString = func(b []byte, s string, paxKeyword string) {
needsPaxHeader := paxKeyword != paxNone && len(s) > len(b) || !isASCII(s)
if needsPaxHeader {
paxHeaders[paxKeyword] = s
return
}
f.formatString(b, s)
}
var formatNumeric = func(b []byte, x int64, paxKeyword string) {
// Try octal first.
s := strconv.FormatInt(x, 8)
if len(s) < len(b) {
f.formatOctal(b, x)
return
}
// If it is too long for octal, and PAX is preferred, use a PAX header.
if paxKeyword != paxNone && tw.preferPax {
f.formatOctal(b, 0)
s := strconv.FormatInt(x, 10)
paxHeaders[paxKeyword] = s
return
}
tw.usedBinary = true
f.formatNumeric(b, x)
}
// keep a reference to the filename to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
pathHeaderBytes := s.next(fileNameSize)
tw.cString(pathHeaderBytes, hdr.Name, true, paxPath, paxHeaders)
formatString(pathHeaderBytes, hdr.Name, paxPath)
// Handle out of range ModTime carefully.
var modTime int64
@ -187,25 +213,25 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
modTime = hdr.ModTime.Unix()
}
tw.octal(s.next(8), hdr.Mode) // 100:108
tw.numeric(s.next(8), int64(hdr.Uid), true, paxUid, paxHeaders) // 108:116
tw.numeric(s.next(8), int64(hdr.Gid), true, paxGid, paxHeaders) // 116:124
tw.numeric(s.next(12), hdr.Size, true, paxSize, paxHeaders) // 124:136
tw.numeric(s.next(12), modTime, false, paxNone, nil) // 136:148 --- consider using pax for finer granularity
s.next(8) // chksum (148:156)
s.next(1)[0] = hdr.Typeflag // 156:157
f.formatOctal(s.next(8), hdr.Mode) // 100:108
formatNumeric(s.next(8), int64(hdr.Uid), paxUid) // 108:116
formatNumeric(s.next(8), int64(hdr.Gid), paxGid) // 116:124
formatNumeric(s.next(12), hdr.Size, paxSize) // 124:136
formatNumeric(s.next(12), modTime, paxNone) // 136:148 --- consider using pax for finer granularity
s.next(8) // chksum (148:156)
s.next(1)[0] = hdr.Typeflag // 156:157
tw.cString(s.next(100), hdr.Linkname, true, paxLinkpath, paxHeaders)
formatString(s.next(100), hdr.Linkname, paxLinkpath)
copy(s.next(8), []byte("ustar\x0000")) // 257:265
tw.cString(s.next(32), hdr.Uname, true, paxUname, paxHeaders) // 265:297
tw.cString(s.next(32), hdr.Gname, true, paxGname, paxHeaders) // 297:329
tw.numeric(s.next(8), hdr.Devmajor, false, paxNone, nil) // 329:337
tw.numeric(s.next(8), hdr.Devminor, false, paxNone, nil) // 337:345
copy(s.next(8), []byte("ustar\x0000")) // 257:265
formatString(s.next(32), hdr.Uname, paxUname) // 265:297
formatString(s.next(32), hdr.Gname, paxGname) // 297:329
formatNumeric(s.next(8), hdr.Devmajor, paxNone) // 329:337
formatNumeric(s.next(8), hdr.Devminor, paxNone) // 337:345
// keep a reference to the prefix to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
prefixHeaderBytes := s.next(155)
tw.cString(prefixHeaderBytes, "", false, paxNone, nil) // 345:500 prefix
formatString(prefixHeaderBytes, "", paxNone) // 345:500 prefix
// Use the GNU magic instead of POSIX magic if we used any GNU extensions.
if tw.usedBinary {
@ -215,37 +241,26 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
_, paxPathUsed := paxHeaders[paxPath]
// try to use a ustar header when only the name is too long
if !tw.preferPax && len(paxHeaders) == 1 && paxPathUsed {
suffix := hdr.Name
prefix := ""
if len(hdr.Name) > fileNameSize && isASCII(hdr.Name) {
var err error
prefix, suffix, err = tw.splitUSTARLongName(hdr.Name)
if err == nil {
// ok we can use a ustar long name instead of pax, now correct the fields
prefix, suffix, ok := splitUSTARPath(hdr.Name)
if ok {
// Since we can encode in USTAR format, disable PAX header.
delete(paxHeaders, paxPath)
// remove the path field from the pax header. this will suppress the pax header
delete(paxHeaders, paxPath)
// update the path fields
tw.cString(pathHeaderBytes, suffix, false, paxNone, nil)
tw.cString(prefixHeaderBytes, prefix, false, paxNone, nil)
// Use the ustar magic if we used ustar long names.
if len(prefix) > 0 && !tw.usedBinary {
copy(header[257:265], []byte("ustar\x00"))
}
}
// Update the path fields
formatString(pathHeaderBytes, suffix, paxNone)
formatString(prefixHeaderBytes, prefix, paxNone)
}
}
// The chksum field is terminated by a NUL and a space.
// This is different from the other octal fields.
chksum, _ := checksum(header)
tw.octal(header[148:155], chksum)
f.formatOctal(header[148:155], chksum) // Never fails
header[155] = ' '
if tw.err != nil {
// problem with header; probably integer too big for a field.
// Check if there were any formatting errors.
if f.err != nil {
tw.err = f.err
return tw.err
}
@ -270,28 +285,25 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
return tw.err
}
// writeUSTARLongName splits a USTAR long name hdr.Name.
// name must be < 256 characters. errNameTooLong is returned
// if hdr.Name can't be split. The splitting heuristic
// is compatible with gnu tar.
func (tw *Writer) splitUSTARLongName(name string) (prefix, suffix string, err error) {
// splitUSTARPath splits a path according to USTAR prefix and suffix rules.
// If the path is not splittable, then it will return ("", "", false).
func splitUSTARPath(name string) (prefix, suffix string, ok bool) {
length := len(name)
if length > fileNamePrefixSize+1 {
if length <= fileNameSize || !isASCII(name) {
return "", "", false
} else if length > fileNamePrefixSize+1 {
length = fileNamePrefixSize + 1
} else if name[length-1] == '/' {
length--
}
i := strings.LastIndex(name[:length], "/")
// nlen contains the resulting length in the name field.
// plen contains the resulting length in the prefix field.
nlen := len(name) - i - 1
plen := i
nlen := len(name) - i - 1 // nlen is length of suffix
plen := i // plen is length of prefix
if i <= 0 || nlen > fileNameSize || nlen == 0 || plen > fileNamePrefixSize {
err = errNameTooLong
return
return "", "", false
}
prefix, suffix = name[:i], name[i+1:]
return
return name[:i], name[i+1:], true
}
// writePaxHeader writes an extended pax header to the
@ -304,11 +316,11 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) erro
// succeed, and seems harmless enough.
ext.ModTime = hdr.ModTime
// The spec asks that we namespace our pseudo files
// with the current pid.
pid := os.Getpid()
// with the current pid. However, this results in differing outputs
// for identical inputs. As such, the constant 0 is now used instead.
// golang.org/issue/12358
dir, file := path.Split(hdr.Name)
fullName := path.Join(dir,
fmt.Sprintf("PaxHeaders.%d", pid), file)
fullName := path.Join(dir, "PaxHeaders.0", file)
ascii := toASCII(fullName)
if len(ascii) > 100 {
@ -318,8 +330,15 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) erro
// Construct the body
var buf bytes.Buffer
for k, v := range paxHeaders {
fmt.Fprint(&buf, paxHeader(k+"="+v))
// Keys are sorted before writing to body to allow deterministic output.
var keys []string
for k := range paxHeaders {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Fprint(&buf, formatPAXRecord(k, paxHeaders[k]))
}
ext.Size = int64(len(buf.Bytes()))
@ -335,17 +354,18 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) erro
return nil
}
// paxHeader formats a single pax record, prefixing it with the appropriate length
func paxHeader(msg string) string {
const padding = 2 // Extra padding for space and newline
size := len(msg) + padding
// formatPAXRecord formats a single PAX record, prefixing it with the
// appropriate length.
func formatPAXRecord(k, v string) string {
const padding = 3 // Extra padding for ' ', '=', and '\n'
size := len(k) + len(v) + padding
size += len(strconv.Itoa(size))
record := fmt.Sprintf("%d %s\n", size, msg)
record := fmt.Sprintf("%d %s=%s\n", size, k, v)
// Final adjustment if adding size field increased the record size.
if len(record) != size {
// Final adjustment if adding size increased
// the number of digits in size
size = len(record)
record = fmt.Sprintf("%d %s\n", size, msg)
record = fmt.Sprintf("%d %s=%s\n", size, k, v)
}
return record
}

View File

@ -9,8 +9,10 @@ import (
"fmt"
"io"
"io/ioutil"
"math"
"os"
"reflect"
"sort"
"strings"
"testing"
"testing/iotest"
@ -291,7 +293,7 @@ func TestPax(t *testing.T) {
t.Fatal(err)
}
// Simple test to make sure PAX extensions are in effect
if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
t.Fatal("Expected at least one PAX header to be written.")
}
// Test that we can get a long name back out of the archive.
@ -330,7 +332,7 @@ func TestPaxSymlink(t *testing.T) {
t.Fatal(err)
}
// Simple test to make sure PAX extensions are in effect
if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
t.Fatal("Expected at least one PAX header to be written.")
}
// Test that we can get a long name back out of the archive.
@ -380,7 +382,7 @@ func TestPaxNonAscii(t *testing.T) {
t.Fatal(err)
}
// Simple test to make sure PAX extensions are in effect
if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
t.Fatal("Expected at least one PAX header to be written.")
}
// Test that we can get a long name back out of the archive.
@ -439,21 +441,49 @@ func TestPaxXattrs(t *testing.T) {
}
}
func TestPAXHeader(t *testing.T) {
medName := strings.Repeat("CD", 50)
longName := strings.Repeat("AB", 100)
paxTests := [][2]string{
{paxPath + "=/etc/hosts", "19 path=/etc/hosts\n"},
{"a=b", "6 a=b\n"}, // Single digit length
{"a=names", "11 a=names\n"}, // Test case involving carries
{paxPath + "=" + longName, fmt.Sprintf("210 path=%s\n", longName)},
{paxPath + "=" + medName, fmt.Sprintf("110 path=%s\n", medName)}}
func TestPaxHeadersSorted(t *testing.T) {
fileinfo, err := os.Stat("testdata/small.txt")
if err != nil {
t.Fatal(err)
}
hdr, err := FileInfoHeader(fileinfo, "")
if err != nil {
t.Fatalf("os.Stat: %v", err)
}
contents := strings.Repeat(" ", int(hdr.Size))
for _, test := range paxTests {
key, expected := test[0], test[1]
if result := paxHeader(key); result != expected {
t.Fatalf("paxHeader: got %s, expected %s", result, expected)
}
hdr.Xattrs = map[string]string{
"foo": "foo",
"bar": "bar",
"baz": "baz",
"qux": "qux",
}
var buf bytes.Buffer
writer := NewWriter(&buf)
if err := writer.WriteHeader(hdr); err != nil {
t.Fatal(err)
}
if _, err = writer.Write([]byte(contents)); err != nil {
t.Fatal(err)
}
if err := writer.Close(); err != nil {
t.Fatal(err)
}
// Simple test to make sure PAX extensions are in effect
if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) {
t.Fatal("Expected at least one PAX header to be written.")
}
// xattr bar should always appear before others
indices := []int{
bytes.Index(buf.Bytes(), []byte("bar=bar")),
bytes.Index(buf.Bytes(), []byte("baz=baz")),
bytes.Index(buf.Bytes(), []byte("foo=foo")),
bytes.Index(buf.Bytes(), []byte("qux=qux")),
}
if !sort.IntsAreSorted(indices) {
t.Fatal("PAX headers are not sorted")
}
}
@ -544,3 +574,149 @@ func TestWriteAfterClose(t *testing.T) {
t.Fatalf("Write: got %v; want ErrWriteAfterClose", err)
}
}
func TestSplitUSTARPath(t *testing.T) {
var sr = strings.Repeat
var vectors = []struct {
input string // Input path
prefix string // Expected output prefix
suffix string // Expected output suffix
ok bool // Split success?
}{
{"", "", "", false},
{"abc", "", "", false},
{"用戶名", "", "", false},
{sr("a", fileNameSize), "", "", false},
{sr("a", fileNameSize) + "/", "", "", false},
{sr("a", fileNameSize) + "/a", sr("a", fileNameSize), "a", true},
{sr("a", fileNamePrefixSize) + "/", "", "", false},
{sr("a", fileNamePrefixSize) + "/a", sr("a", fileNamePrefixSize), "a", true},
{sr("a", fileNameSize+1), "", "", false},
{sr("/", fileNameSize+1), sr("/", fileNameSize-1), "/", true},
{sr("a", fileNamePrefixSize) + "/" + sr("b", fileNameSize),
sr("a", fileNamePrefixSize), sr("b", fileNameSize), true},
{sr("a", fileNamePrefixSize) + "//" + sr("b", fileNameSize), "", "", false},
{sr("a/", fileNameSize), sr("a/", 77) + "a", sr("a/", 22), true},
}
for _, v := range vectors {
prefix, suffix, ok := splitUSTARPath(v.input)
if prefix != v.prefix || suffix != v.suffix || ok != v.ok {
t.Errorf("splitUSTARPath(%q):\ngot (%q, %q, %v)\nwant (%q, %q, %v)",
v.input, prefix, suffix, ok, v.prefix, v.suffix, v.ok)
}
}
}
func TestFormatPAXRecord(t *testing.T) {
var medName = strings.Repeat("CD", 50)
var longName = strings.Repeat("AB", 100)
var vectors = []struct {
inputKey string
inputVal string
output string
}{
{"k", "v", "6 k=v\n"},
{"path", "/etc/hosts", "19 path=/etc/hosts\n"},
{"path", longName, "210 path=" + longName + "\n"},
{"path", medName, "110 path=" + medName + "\n"},
{"foo", "ba", "9 foo=ba\n"},
{"foo", "bar", "11 foo=bar\n"},
{"foo", "b=\nar=\n==\x00", "18 foo=b=\nar=\n==\x00\n"},
{"foo", "hello9 foo=ba\nworld", "27 foo=hello9 foo=ba\nworld\n"},
{"☺☻☹", "日a本b語ç", "27 ☺☻☹=日a本b語ç\n"},
{"\x00hello", "\x00world", "17 \x00hello=\x00world\n"},
}
for _, v := range vectors {
output := formatPAXRecord(v.inputKey, v.inputVal)
if output != v.output {
t.Errorf("formatPAXRecord(%q, %q): got %q, want %q",
v.inputKey, v.inputVal, output, v.output)
}
}
}
func TestFitsInBase256(t *testing.T) {
var vectors = []struct {
input int64
width int
ok bool
}{
{+1, 8, true},
{0, 8, true},
{-1, 8, true},
{1 << 56, 8, false},
{(1 << 56) - 1, 8, true},
{-1 << 56, 8, true},
{(-1 << 56) - 1, 8, false},
{121654, 8, true},
{-9849849, 8, true},
{math.MaxInt64, 9, true},
{0, 9, true},
{math.MinInt64, 9, true},
{math.MaxInt64, 12, true},
{0, 12, true},
{math.MinInt64, 12, true},
}
for _, v := range vectors {
ok := fitsInBase256(v.width, v.input)
if ok != v.ok {
t.Errorf("checkNumeric(%d, %d): got %v, want %v", v.input, v.width, ok, v.ok)
}
}
}
func TestFormatNumeric(t *testing.T) {
var vectors = []struct {
input int64
output string
ok bool
}{
// Test base-256 (binary) encoded values.
{-1, "\xff", true},
{-1, "\xff\xff", true},
{-1, "\xff\xff\xff", true},
{(1 << 0), "0", false},
{(1 << 8) - 1, "\x80\xff", true},
{(1 << 8), "0\x00", false},
{(1 << 16) - 1, "\x80\xff\xff", true},
{(1 << 16), "00\x00", false},
{-1 * (1 << 0), "\xff", true},
{-1*(1<<0) - 1, "0", false},
{-1 * (1 << 8), "\xff\x00", true},
{-1*(1<<8) - 1, "0\x00", false},
{-1 * (1 << 16), "\xff\x00\x00", true},
{-1*(1<<16) - 1, "00\x00", false},
{537795476381659745, "0000000\x00", false},
{537795476381659745, "\x80\x00\x00\x00\x07\x76\xa2\x22\xeb\x8a\x72\x61", true},
{-615126028225187231, "0000000\x00", false},
{-615126028225187231, "\xff\xff\xff\xff\xf7\x76\xa2\x22\xeb\x8a\x72\x61", true},
{math.MaxInt64, "0000000\x00", false},
{math.MaxInt64, "\x80\x00\x00\x00\x7f\xff\xff\xff\xff\xff\xff\xff", true},
{math.MinInt64, "0000000\x00", false},
{math.MinInt64, "\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00", true},
{math.MaxInt64, "\x80\x7f\xff\xff\xff\xff\xff\xff\xff", true},
{math.MinInt64, "\xff\x80\x00\x00\x00\x00\x00\x00\x00", true},
}
for _, v := range vectors {
var f formatter
output := make([]byte, len(v.output))
f.formatNumeric(output, v.input)
ok := (f.err == nil)
if ok != v.ok {
if v.ok {
t.Errorf("formatNumeric(%d): got formatting failure, want success", v.input)
} else {
t.Errorf("formatNumeric(%d): got formatting success, want failure", v.input)
}
}
if string(output) != v.output {
t.Errorf("formatNumeric(%d): got %q, want %q", v.input, output, v.output)
}
}
}

View File

@ -22,9 +22,10 @@ var (
)
type Reader struct {
r io.ReaderAt
File []*File
Comment string
r io.ReaderAt
File []*File
Comment string
decompressors map[uint16]Decompressor
}
type ReadCloser struct {
@ -34,6 +35,7 @@ type ReadCloser struct {
type File struct {
FileHeader
zip *Reader
zipr io.ReaderAt
zipsize int64
headerOffset int64
@ -95,7 +97,7 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
// a bad one, and then only report a ErrFormat or UnexpectedEOF if
// the file count modulo 65536 is incorrect.
for {
f := &File{zipr: r, zipsize: size}
f := &File{zip: z, zipr: r, zipsize: size}
err = readDirectoryHeader(f, buf)
if err == ErrFormat || err == io.ErrUnexpectedEOF {
break
@ -113,6 +115,24 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
return nil
}
// RegisterDecompressor registers or overrides a custom decompressor for a
// specific method ID. If a decompressor for a given method is not found,
// Reader will default to looking up the decompressor at the package level.
func (z *Reader) RegisterDecompressor(method uint16, dcomp Decompressor) {
if z.decompressors == nil {
z.decompressors = make(map[uint16]Decompressor)
}
z.decompressors[method] = dcomp
}
func (z *Reader) decompressor(method uint16) Decompressor {
dcomp := z.decompressors[method]
if dcomp == nil {
dcomp = decompressor(method)
}
return dcomp
}
// Close closes the Zip file, rendering it unusable for I/O.
func (rc *ReadCloser) Close() error {
return rc.f.Close()
@ -140,7 +160,7 @@ func (f *File) Open() (rc io.ReadCloser, err error) {
}
size := int64(f.CompressedSize64)
r := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size)
dcomp := decompressor(f.Method)
dcomp := f.zip.decompressor(f.Method)
if dcomp == nil {
err = ErrAlgorithm
return
@ -261,39 +281,59 @@ func readDirectoryHeader(f *File, r io.Reader) error {
f.Extra = d[filenameLen : filenameLen+extraLen]
f.Comment = string(d[filenameLen+extraLen:])
needUSize := f.UncompressedSize == ^uint32(0)
needCSize := f.CompressedSize == ^uint32(0)
needHeaderOffset := f.headerOffset == int64(^uint32(0))
if len(f.Extra) > 0 {
// Best effort to find what we need.
// Other zip authors might not even follow the basic format,
// and we'll just ignore the Extra content in that case.
b := readBuf(f.Extra)
for len(b) >= 4 { // need at least tag and size
tag := b.uint16()
size := b.uint16()
if int(size) > len(b) {
return ErrFormat
break
}
if tag == zip64ExtraId {
// update directory values from the zip64 extra block
// update directory values from the zip64 extra block.
// They should only be consulted if the sizes read earlier
// are maxed out.
// See golang.org/issue/13367.
eb := readBuf(b[:size])
if len(eb) >= 8 {
if needUSize {
needUSize = false
if len(eb) < 8 {
return ErrFormat
}
f.UncompressedSize64 = eb.uint64()
}
if len(eb) >= 8 {
if needCSize {
needCSize = false
if len(eb) < 8 {
return ErrFormat
}
f.CompressedSize64 = eb.uint64()
}
if len(eb) >= 8 {
if needHeaderOffset {
needHeaderOffset = false
if len(eb) < 8 {
return ErrFormat
}
f.headerOffset = int64(eb.uint64())
}
break
}
b = b[size:]
}
// Should have consumed the whole header.
// But popular zip & JAR creation tools are broken and
// may pad extra zeros at the end, so accept those
// too. See golang.org/issue/8186.
for _, v := range b {
if v != 0 {
return ErrFormat
}
}
}
if needUSize || needCSize || needHeaderOffset {
return ErrFormat
}
return nil
}
@ -376,14 +416,16 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error)
}
d.comment = string(b[:l])
p, err := findDirectory64End(r, directoryEndOffset)
if err == nil && p >= 0 {
err = readDirectory64End(r, p, d)
// These values mean that the file can be a zip64 file
if d.directoryRecords == 0xffff || d.directorySize == 0xffff || d.directoryOffset == 0xffffffff {
p, err := findDirectory64End(r, directoryEndOffset)
if err == nil && p >= 0 {
err = readDirectory64End(r, p, d)
}
if err != nil {
return nil, err
}
}
if err != nil {
return nil, err
}
// Make sure directoryOffset points to somewhere in our file.
if o := int64(d.directoryOffset); o < 0 || o >= size {
return nil, ErrFormat
@ -407,8 +449,13 @@ func findDirectory64End(r io.ReaderAt, directoryEndOffset int64) (int64, error)
if sig := b.uint32(); sig != directory64LocSignature {
return -1, nil
}
b = b[4:] // skip number of the disk with the start of the zip64 end of central directory
p := b.uint64() // relative offset of the zip64 end of central directory record
if b.uint32() != 0 { // number of the disk with the start of the zip64 end of central directory
return -1, nil // the file is not a valid zip64-file
}
p := b.uint64() // relative offset of the zip64 end of central directory record
if b.uint32() != 1 { // total number of disks
return -1, nil // the file is not a valid zip64-file
}
return int64(p), nil
}

View File

@ -605,3 +605,40 @@ func TestIssue11146(t *testing.T) {
}
r.Close()
}
// Verify we do not treat non-zip64 archives as zip64
func TestIssue12449(t *testing.T) {
data := []byte{
0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00,
0x00, 0x00, 0x6b, 0xb4, 0xba, 0x46, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x00, 0x18, 0x00, 0xca, 0x64,
0x55, 0x75, 0x78, 0x0b, 0x00, 0x50, 0x4b, 0x05,
0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
0x00, 0x49, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00,
0x00, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x0a,
0x50, 0x4b, 0x07, 0x08, 0x1d, 0x88, 0x77, 0xb0,
0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x14, 0x00,
0x08, 0x00, 0x00, 0x00, 0x6b, 0xb4, 0xba, 0x46,
0x1d, 0x88, 0x77, 0xb0, 0x07, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x18, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xa0, 0x81, 0x00, 0x00, 0x00, 0x00, 0xca, 0x64,
0x55, 0x75, 0x78, 0x0b, 0x00, 0x50, 0x4b, 0x05,
0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
0x00, 0x49, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00,
0x00, 0x97, 0x2b, 0x49, 0x23, 0x05, 0xc5, 0x0b,
0xa7, 0xd1, 0x52, 0xa2, 0x9c, 0x50, 0x4b, 0x06,
0x07, 0xc8, 0x19, 0xc1, 0xaf, 0x94, 0x9c, 0x61,
0x44, 0xbe, 0x94, 0x19, 0x42, 0x58, 0x12, 0xc6,
0x5b, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x01, 0x00, 0x69, 0x00, 0x00,
0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00,
}
// Read in the archive.
_, err := NewReader(bytes.NewReader([]byte(data)), int64(len(data)))
if err != nil {
t.Errorf("Error reading the archive: %v", err)
}
}

View File

@ -12,15 +12,19 @@ import (
"sync"
)
// A Compressor returns a compressing writer, writing to the
// provided writer. On Close, any pending data should be flushed.
type Compressor func(io.Writer) (io.WriteCloser, error)
// A Compressor returns a new compressing writer, writing to w.
// The WriteCloser's Close method must be used to flush pending data to w.
// The Compressor itself must be safe to invoke from multiple goroutines
// simultaneously, but each returned writer will be used only by
// one goroutine at a time.
type Compressor func(w io.Writer) (io.WriteCloser, error)
// Decompressor is a function that wraps a Reader with a decompressing Reader.
// The decompressed ReadCloser is returned to callers who open files from
// within the archive. These callers are responsible for closing this reader
// when they're finished reading.
type Decompressor func(io.Reader) io.ReadCloser
// A Decompressor returns a new decompressing reader, reading from r.
// The ReadCloser's Close method must be used to release associated resources.
// The Decompressor itself must be safe to invoke from multiple goroutines
// simultaneously, but each returned reader will be used only by
// one goroutine at a time.
type Decompressor func(r io.Reader) io.ReadCloser
var flateWriterPool sync.Pool
@ -75,14 +79,15 @@ var (
)
// RegisterDecompressor allows custom decompressors for a specified method ID.
func RegisterDecompressor(method uint16, d Decompressor) {
// The common methods Store and Deflate are built in.
func RegisterDecompressor(method uint16, dcomp Decompressor) {
mu.Lock()
defer mu.Unlock()
if _, ok := decompressors[method]; ok {
panic("decompressor already registered")
}
decompressors[method] = d
decompressors[method] = dcomp
}
// RegisterCompressor registers custom compressors for a specified method ID.

View File

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

View File

@ -14,14 +14,14 @@ import (
)
// TODO(adg): support zip file comments
// TODO(adg): support specifying deflate level
// Writer implements a zip file writer.
type Writer struct {
cw *countWriter
dir []*header
last *fileWriter
closed bool
cw *countWriter
dir []*header
last *fileWriter
closed bool
compressors map[uint16]Compressor
}
type header struct {
@ -78,7 +78,7 @@ func (w *Writer) Close() error {
b.uint16(h.ModifiedTime)
b.uint16(h.ModifiedDate)
b.uint32(h.CRC32)
if h.isZip64() || h.offset > uint32max {
if h.isZip64() || h.offset >= uint32max {
// the file needs a zip64 header. store maxint in both
// 32 bit size fields (and offset later) to signal that the
// zip64 extra header should be used.
@ -220,7 +220,7 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
compCount: &countWriter{w: w.cw},
crc32: crc32.NewIEEE(),
}
comp := compressor(fh.Method)
comp := w.compressor(fh.Method)
if comp == nil {
return nil, ErrAlgorithm
}
@ -270,6 +270,24 @@ func writeHeader(w io.Writer, h *FileHeader) error {
return err
}
// RegisterCompressor registers or overrides a custom compressor for a specific
// method ID. If a compressor for a given method is not found, Writer will
// default to looking up the compressor at the package level.
func (w *Writer) RegisterCompressor(method uint16, comp Compressor) {
if w.compressors == nil {
w.compressors = make(map[uint16]Compressor)
}
w.compressors[method] = comp
}
func (w *Writer) compressor(method uint16) Compressor {
comp := w.compressors[method]
if comp == nil {
comp = compressor(method)
}
return comp
}
type fileWriter struct {
*header
zipw io.Writer

View File

@ -10,6 +10,7 @@ import (
"bytes"
"fmt"
"hash"
"internal/testenv"
"io"
"io/ioutil"
"sort"
@ -19,6 +20,9 @@ import (
)
func TestOver65kFiles(t *testing.T) {
if testing.Short() && testenv.Builder() == "" {
t.Skip("skipping in short mode")
}
buf := new(bytes.Buffer)
w := NewWriter(buf)
const nFiles = (1 << 16) + 42
@ -233,10 +237,24 @@ func TestZip64(t *testing.T) {
testZip64DirectoryRecordLength(buf, t)
}
func TestZip64EdgeCase(t *testing.T) {
if testing.Short() {
t.Skip("slow test; skipping")
}
// Test a zip file with uncompressed size 0xFFFFFFFF.
// That's the magic marker for a 64-bit file, so even though
// it fits in a 32-bit field we must use the 64-bit field.
// Go 1.5 and earlier got this wrong,
// writing an invalid zip file.
const size = 1<<32 - 1 - int64(len("END\n")) // before the "END\n" part
buf := testZip64(t, size)
testZip64DirectoryRecordLength(buf, t)
}
func testZip64(t testing.TB, size int64) *rleBuffer {
const chunkSize = 1024
chunks := int(size / chunkSize)
// write 2^32 bytes plus "END\n" to a zip file
// write size bytes plus "END\n" to a zip file
buf := new(rleBuffer)
w := NewWriter(buf)
f, err := w.CreateHeader(&FileHeader{
@ -257,6 +275,12 @@ func testZip64(t testing.TB, size int64) *rleBuffer {
t.Fatal("write chunk:", err)
}
}
if frag := int(size % chunkSize); frag > 0 {
_, err := f.Write(chunk[:frag])
if err != nil {
t.Fatal("write chunk:", err)
}
}
end := []byte("END\n")
_, err = f.Write(end)
if err != nil {
@ -283,6 +307,12 @@ func testZip64(t testing.TB, size int64) *rleBuffer {
t.Fatal("read:", err)
}
}
if frag := int(size % chunkSize); frag > 0 {
_, err := io.ReadFull(rc, chunk[:frag])
if err != nil {
t.Fatal("read:", err)
}
}
gotEnd, err := ioutil.ReadAll(rc)
if err != nil {
t.Fatal("read end:", err)
@ -294,14 +324,14 @@ func testZip64(t testing.TB, size int64) *rleBuffer {
if err != nil {
t.Fatal("closing:", err)
}
if size == 1<<32 {
if size+int64(len("END\n")) >= 1<<32-1 {
if got, want := f0.UncompressedSize, uint32(uint32max); got != want {
t.Errorf("UncompressedSize %d, want %d", got, want)
t.Errorf("UncompressedSize %#x, want %#x", got, want)
}
}
if got, want := f0.UncompressedSize64, uint64(size)+uint64(len(end)); got != want {
t.Errorf("UncompressedSize64 %d, want %d", got, want)
t.Errorf("UncompressedSize64 %#x, want %#x", got, want)
}
return buf
@ -373,9 +403,14 @@ func testValidHeader(h *FileHeader, t *testing.T) {
}
b := buf.Bytes()
if _, err = NewReader(bytes.NewReader(b), int64(len(b))); err != nil {
zf, err := NewReader(bytes.NewReader(b), int64(len(b)))
if err != nil {
t.Fatalf("got %v, expected nil", err)
}
zh := zf.File[0].FileHeader
if zh.Name != h.Name || zh.Method != h.Method || zh.UncompressedSize64 != uint64(len("hi")) {
t.Fatalf("got %q/%d/%d expected %q/%d/%d", zh.Name, zh.Method, zh.UncompressedSize64, h.Name, h.Method, len("hi"))
}
}
// Issue 4302.
@ -388,20 +423,29 @@ func TestHeaderInvalidTagAndSize(t *testing.T) {
h := FileHeader{
Name: filename,
Method: Deflate,
Extra: []byte(ts.Format(time.RFC3339Nano)), // missing tag and len
Extra: []byte(ts.Format(time.RFC3339Nano)), // missing tag and len, but Extra is best-effort parsing
}
h.SetModTime(ts)
testInvalidHeader(&h, t)
testValidHeader(&h, t)
}
func TestHeaderTooShort(t *testing.T) {
h := FileHeader{
Name: "foo.txt",
Method: Deflate,
Extra: []byte{zip64ExtraId}, // missing size
Extra: []byte{zip64ExtraId}, // missing size and second half of tag, but Extra is best-effort parsing
}
testInvalidHeader(&h, t)
testValidHeader(&h, t)
}
func TestHeaderIgnoredSize(t *testing.T) {
h := FileHeader{
Name: "foo.txt",
Method: Deflate,
Extra: []byte{zip64ExtraId & 0xFF, zip64ExtraId >> 8, 24, 0, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8}, // bad size but shouldn't be consulted
}
testValidHeader(&h, t)
}
// Issue 4393. It is valid to have an extra data header

View File

@ -179,7 +179,7 @@ func (b *Reader) Discard(n int) (discarded int, err error) {
// Read reads data into p.
// It returns the number of bytes read into p.
// It calls Read at most once on the underlying Reader,
// The bytes are taken from at most one Read on the underlying Reader,
// hence n may be less than len(p).
// At EOF, the count will be zero and err will be io.EOF.
func (b *Reader) Read(p []byte) (n int, err error) {

View File

@ -80,3 +80,32 @@ func ExampleScanner_custom() {
// 5678
// Invalid input: strconv.ParseInt: parsing "1234567901234567890": value out of range
}
// Use a Scanner with a custom split function to parse a comma-separated
// list with an empty final value.
func ExampleScanner_emptyFinalToken() {
// Comma-separated list; last entry is empty.
const input = "1,2,3,4,"
scanner := bufio.NewScanner(strings.NewReader(input))
// Define a split function that separates on commas.
onComma := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
for i := 0; i < len(data); i++ {
if data[i] == ',' {
return i + 1, data[:i], nil
}
}
// There is one final token to be delivered, which may be the empty string.
// Returning bufio.ErrFinalToken here tells Scan there are no more tokens after this
// but does not trigger an error to be returned from Scan itself.
return 0, data, bufio.ErrFinalToken
}
scanner.Split(onComma)
// Scan.
for scanner.Scan() {
fmt.Printf("%q ", scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading input:", err)
}
// Output: "1" "2" "3" "4" ""
}

View File

@ -37,6 +37,8 @@ type Scanner struct {
end int // End of data in buf.
err error // Sticky error.
empties int // Count of successive empty tokens.
scanCalled bool // Scan has been called; buffer is in use.
done bool // Scan has finished.
}
// SplitFunc is the signature of the split function used to tokenize the
@ -65,10 +67,13 @@ var (
)
const (
// MaxScanTokenSize is the maximum size used to buffer a token.
// MaxScanTokenSize is the maximum size used to buffer a token
// unless the user provides an explicit buffer with Scan.Buffer.
// The actual maximum token size may be smaller as the buffer
// may need to include, for instance, a newline.
MaxScanTokenSize = 64 * 1024
startBufSize = 4096 // Size of initial allocation for buffer.
)
// NewScanner returns a new Scanner to read from r.
@ -78,7 +83,6 @@ func NewScanner(r io.Reader) *Scanner {
r: r,
split: ScanLines,
maxTokenSize: MaxScanTokenSize,
buf: make([]byte, 4096), // Plausible starting size; needn't be large.
}
}
@ -103,6 +107,16 @@ func (s *Scanner) Text() string {
return string(s.token)
}
// ErrFinalToken is a special sentinel error value. It is intended to be
// returned by a Split function to indicate that the token being delivered
// with the error is the last token and scanning should stop after this one.
// After ErrFinalToken is received by Scan, scanning stops with no error.
// The value is useful to stop processing early or when it is necessary to
// deliver a final empty token. One could achieve the same behavior
// with a custom error value but providing one here is tidier.
// See the emptyFinalToken example for a use of this value.
var ErrFinalToken = errors.New("final token")
// Scan advances the Scanner to the next token, which will then be
// available through the Bytes or Text method. It returns false when the
// scan stops, either by reaching the end of the input or an error.
@ -112,6 +126,10 @@ func (s *Scanner) Text() string {
// Scan panics if the split function returns 100 empty tokens without
// advancing the input. This is a common error mode for scanners.
func (s *Scanner) Scan() bool {
if s.done {
return false
}
s.scanCalled = true
// Loop until we have a token.
for {
// See if we can get a token with what we already have.
@ -120,6 +138,11 @@ func (s *Scanner) Scan() bool {
if s.end > s.start || s.err != nil {
advance, token, err := s.split(s.buf[s.start:s.end], s.err != nil)
if err != nil {
if err == ErrFinalToken {
s.token = token
s.done = true
return true
}
s.setErr(err)
return false
}
@ -158,11 +181,16 @@ func (s *Scanner) Scan() bool {
}
// Is the buffer full? If so, resize.
if s.end == len(s.buf) {
if len(s.buf) >= s.maxTokenSize {
// Guarantee no overflow in the multiplication below.
const maxInt = int(^uint(0) >> 1)
if len(s.buf) >= s.maxTokenSize || len(s.buf) > maxInt/2 {
s.setErr(ErrTooLong)
return false
}
newSize := len(s.buf) * 2
if newSize == 0 {
newSize = startBufSize
}
if newSize > s.maxTokenSize {
newSize = s.maxTokenSize
}
@ -217,9 +245,31 @@ func (s *Scanner) setErr(err error) {
}
}
// Split sets the split function for the Scanner. If called, it must be
// called before Scan. The default split function is ScanLines.
// Buffer sets the initial buffer to use when scanning and the maximum
// size of buffer that may be allocated during scanning. The maximum
// token size is the larger of max and cap(buf). If max <= cap(buf),
// Scan will use this buffer only and do no allocation.
//
// By default, Scan uses an internal buffer and sets the
// maximum token size to MaxScanTokenSize.
//
// Buffer panics if it is called after scanning has started.
func (s *Scanner) Buffer(buf []byte, max int) {
if s.scanCalled {
panic("Buffer called after Scan")
}
s.buf = buf[0:cap(buf)]
s.maxTokenSize = max
}
// Split sets the split function for the Scanner.
// The default split function is ScanLines.
//
// Split panics if it is called after scanning has started.
func (s *Scanner) Split(split SplitFunc) {
if s.scanCalled {
panic("Split called after Scan")
}
s.split = split
}

View File

@ -429,33 +429,37 @@ func commaSplit(data []byte, atEOF bool) (advance int, token []byte, err error)
return i + 1, data[:i], nil
}
}
if !atEOF {
return 0, nil, nil
}
return 0, data, nil
return 0, data, ErrFinalToken
}
func TestEmptyTokens(t *testing.T) {
s := NewScanner(strings.NewReader("1,2,3,"))
values := []string{"1", "2", "3", ""}
func testEmptyTokens(t *testing.T, text string, values []string) {
s := NewScanner(strings.NewReader(text))
s.Split(commaSplit)
var i int
for i = 0; i < len(values); i++ {
if !s.Scan() {
break
for i = 0; s.Scan(); i++ {
if i >= len(values) {
t.Fatalf("got %d fields, expected %d", i+1, len(values))
}
if s.Text() != values[i] {
t.Errorf("%d: expected %q got %q", i, values[i], s.Text())
}
}
if i != len(values) {
t.Errorf("got %d fields, expected %d", i, len(values))
t.Fatalf("got %d fields, expected %d", i, len(values))
}
if err := s.Err(); err != nil {
t.Fatal(err)
}
}
func TestEmptyTokens(t *testing.T) {
testEmptyTokens(t, "1,2,3,", []string{"1", "2", "3", ""})
}
func TestWithNoEmptyTokens(t *testing.T) {
testEmptyTokens(t, "1,2,3", []string{"1", "2", "3"})
}
func loopAtEOFSplit(data []byte, atEOF bool) (advance int, token []byte, err error) {
if len(data) > 0 {
return 1, data[:1], nil
@ -522,3 +526,19 @@ func TestEmptyLinesOK(t *testing.T) {
t.Fatalf("stopped with %d left to process", c)
}
}
// Make sure we can read a huge token if a big enough buffer is provided.
func TestHugeBuffer(t *testing.T) {
text := strings.Repeat("x", 2*MaxScanTokenSize)
s := NewScanner(strings.NewReader(text + "\n"))
s.Buffer(make([]byte, 100), 3*MaxScanTokenSize)
for s.Scan() {
token := s.Text()
if token != text {
t.Errorf("scan got incorrect token of length %d", len(token))
}
}
if s.Err() != nil {
t.Fatal("after scan:", s.Err())
}
}

View File

@ -36,10 +36,11 @@ const (
// ErrTooLarge is passed to panic if memory cannot be allocated to store data in a buffer.
var ErrTooLarge = errors.New("bytes.Buffer: too large")
// Bytes returns a slice of the contents of the unread portion of the buffer;
// len(b.Bytes()) == b.Len(). If the caller changes the contents of the
// returned slice, the contents of the buffer will change provided there
// are no intervening method calls on the Buffer.
// Bytes returns a slice of length b.Len() holding the unread portion of the buffer.
// The slice is valid for use only until the next buffer modification (that is,
// only until the next call to a method like Read, Write, Reset, or Truncate).
// The slice aliases the buffer content at least until the next buffer modification,
// so immediate changes to the slice will affect the result of future reads.
func (b *Buffer) Bytes() []byte { return b.buf[b.off:] }
// String returns the contents of the unread portion of the buffer
@ -60,7 +61,8 @@ func (b *Buffer) Len() int { return len(b.buf) - b.off }
// total space allocated for the buffer's data.
func (b *Buffer) Cap() int { return cap(b.buf) }
// Truncate discards all but the first n unread bytes from the buffer.
// Truncate discards all but the first n unread bytes from the buffer
// but continues to use the same allocated storage.
// It panics if n is negative or greater than the length of the buffer.
func (b *Buffer) Truncate(n int) {
b.lastRead = opInvalid
@ -74,8 +76,9 @@ func (b *Buffer) Truncate(n int) {
b.buf = b.buf[0 : b.off+n]
}
// Reset resets the buffer so it has no content.
// b.Reset() is the same as b.Truncate(0).
// Reset resets the buffer to be empty,
// but it retains the underlying storage for use by future writes.
// Reset is the same as Truncate(0).
func (b *Buffer) Reset() { b.Truncate(0) }
// grow grows the buffer to guarantee space for n more bytes.

View File

@ -1255,3 +1255,34 @@ func BenchmarkRepeat(b *testing.B) {
Repeat([]byte("-"), 80)
}
}
func benchmarkBytesCompare(b *testing.B, n int) {
var x = make([]byte, n)
var y = make([]byte, n)
for i := 0; i < n; i++ {
x[i] = 'a'
}
for i := 0; i < n; i++ {
y[i] = 'a'
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
Compare(x, y)
}
}
func BenchmarkBytesCompare1(b *testing.B) { benchmarkBytesCompare(b, 1) }
func BenchmarkBytesCompare2(b *testing.B) { benchmarkBytesCompare(b, 2) }
func BenchmarkBytesCompare4(b *testing.B) { benchmarkBytesCompare(b, 4) }
func BenchmarkBytesCompare8(b *testing.B) { benchmarkBytesCompare(b, 8) }
func BenchmarkBytesCompare16(b *testing.B) { benchmarkBytesCompare(b, 16) }
func BenchmarkBytesCompare32(b *testing.B) { benchmarkBytesCompare(b, 32) }
func BenchmarkBytesCompare64(b *testing.B) { benchmarkBytesCompare(b, 64) }
func BenchmarkBytesCompare128(b *testing.B) { benchmarkBytesCompare(b, 128) }
func BenchmarkBytesCompare256(b *testing.B) { benchmarkBytesCompare(b, 256) }
func BenchmarkBytesCompare512(b *testing.B) { benchmarkBytesCompare(b, 512) }
func BenchmarkBytesCompare1024(b *testing.B) { benchmarkBytesCompare(b, 1024) }
func BenchmarkBytesCompare2048(b *testing.B) { benchmarkBytesCompare(b, 2048) }

View File

@ -124,7 +124,7 @@ func (f *File) ReadGo(name string) {
if f.Ref == nil {
f.Ref = make([]*Ref, 0, 8)
}
f.walk(ast2, "prog", (*File).saveRef)
f.walk(ast2, "prog", (*File).saveExprs)
// Accumulate exported functions.
// The comments are only on ast1 but we need to
@ -163,52 +163,72 @@ func commentText(g *ast.CommentGroup) string {
return strings.Join(pieces, "")
}
// Save various references we are going to need later.
func (f *File) saveExprs(x interface{}, context string) {
switch x := x.(type) {
case *ast.Expr:
switch (*x).(type) {
case *ast.SelectorExpr:
f.saveRef(x, context)
}
case *ast.CallExpr:
f.saveCall(x)
}
}
// Save references to C.xxx for later processing.
func (f *File) saveRef(x interface{}, context string) {
n, ok := x.(*ast.Expr)
func (f *File) saveRef(n *ast.Expr, context string) {
sel := (*n).(*ast.SelectorExpr)
// For now, assume that the only instance of capital C is when
// used as the imported package identifier.
// The parser should take care of scoping in the future, so
// that we will be able to distinguish a "top-level C" from a
// local C.
if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
return
}
if context == "as2" {
context = "expr"
}
if context == "embed-type" {
error_(sel.Pos(), "cannot embed C type")
}
goname := sel.Sel.Name
if goname == "errno" {
error_(sel.Pos(), "cannot refer to errno directly; see documentation")
return
}
if goname == "_CMalloc" {
error_(sel.Pos(), "cannot refer to C._CMalloc; use C.malloc")
return
}
if goname == "malloc" {
goname = "_CMalloc"
}
name := f.Name[goname]
if name == nil {
name = &Name{
Go: goname,
}
f.Name[goname] = name
}
f.Ref = append(f.Ref, &Ref{
Name: name,
Expr: n,
Context: context,
})
}
// Save calls to C.xxx for later processing.
func (f *File) saveCall(call *ast.CallExpr) {
sel, ok := call.Fun.(*ast.SelectorExpr)
if !ok {
return
}
if sel, ok := (*n).(*ast.SelectorExpr); ok {
// For now, assume that the only instance of capital C is
// when used as the imported package identifier.
// The parser should take care of scoping in the future,
// so that we will be able to distinguish a "top-level C"
// from a local C.
if l, ok := sel.X.(*ast.Ident); ok && l.Name == "C" {
if context == "as2" {
context = "expr"
}
if context == "embed-type" {
error_(sel.Pos(), "cannot embed C type")
}
goname := sel.Sel.Name
if goname == "errno" {
error_(sel.Pos(), "cannot refer to errno directly; see documentation")
return
}
if goname == "_CMalloc" {
error_(sel.Pos(), "cannot refer to C._CMalloc; use C.malloc")
return
}
if goname == "malloc" {
goname = "_CMalloc"
}
name := f.Name[goname]
if name == nil {
name = &Name{
Go: goname,
}
f.Name[goname] = name
}
f.Ref = append(f.Ref, &Ref{
Name: name,
Expr: n,
Context: context,
})
return
}
if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
return
}
f.Calls = append(f.Calls, call)
}
// If a function should be exported add it to ExpFunc.

View File

@ -117,17 +117,27 @@ The standard C numeric types are available under the names
C.char, C.schar (signed char), C.uchar (unsigned char),
C.short, C.ushort (unsigned short), C.int, C.uint (unsigned int),
C.long, C.ulong (unsigned long), C.longlong (long long),
C.ulonglong (unsigned long long), C.float, C.double.
C.ulonglong (unsigned long long), C.float, C.double,
C.complexfloat (complex float), and C.complexdouble (complex double).
The C type void* is represented by Go's unsafe.Pointer.
The C types __int128_t and __uint128_t are represented by [16]byte.
To access a struct, union, or enum type directly, prefix it with
struct_, union_, or enum_, as in C.struct_stat.
The size of any C type T is available as C.sizeof_T, as in
C.sizeof_struct_stat.
As Go doesn't have support for C's union type in the general case,
C's union types are represented as a Go byte array with the same length.
Go structs cannot embed fields with C types.
Go code can not refer to zero-sized fields that occur at the end of
non-empty C structs. To get the address of such a field (which is the
only operation you can do with a zero-sized field) you must take the
address of the struct and add the size of the struct.
Cgo translates C types into equivalent unexported Go types.
Because the translations are unexported, a Go package should not
expose C types in its exported API: a C type used in one Go package
@ -188,10 +198,10 @@ by making copies of the data. In pseudo-Go definitions:
// C string to Go string
func C.GoString(*C.char) string
// C string, length to Go string
// C data with explicit length to Go string
func C.GoStringN(*C.char, C.int) string
// C pointer, length to Go []byte
// C data with explicit length to Go []byte
func C.GoBytes(unsafe.Pointer, C.int) []byte
C references to Go
@ -221,6 +231,55 @@ definitions and declarations, then the two output files will produce
duplicate symbols and the linker will fail. To avoid this, definitions
must be placed in preambles in other files, or in C source files.
Passing pointers
Go is a garbage collected language, and the garbage collector needs to
know the location of every pointer to Go memory. Because of this,
there are restrictions on passing pointers between Go and C.
In this section the term Go pointer means a pointer to memory
allocated by Go (such as by using the & operator or calling the
predefined new function) and the term C pointer means a pointer to
memory allocated by C (such as by a call to C.malloc). Whether a
pointer is a Go pointer or a C pointer is a dynamic property
determined by how the memory was allocated; it has nothing to do with
the type of the pointer.
Go code may pass a Go pointer to C provided the Go memory to which it
points does not contain any Go pointers. The C code must preserve
this property: it must not store any Go pointers in Go memory, even
temporarily. When passing a pointer to a field in a struct, the Go
memory in question is the memory occupied by the field, not the entire
struct. When passing a pointer to an element in an array or slice,
the Go memory in question is the entire array or the entire backing
array of the slice.
C code may not keep a copy of a Go pointer after the call returns.
A Go function called by C code may not return a Go pointer. A Go
function called by C code may take C pointers as arguments, and it may
store non-pointer or C pointer data through those pointers, but it may
not store a Go pointer in memory pointed to by a C pointer. A Go
function called by C code may take a Go pointer as an argument, but it
must preserve the property that the Go memory to which it points does
not contain any Go pointers.
Go code may not store a Go pointer in C memory. C code may store Go
pointers in C memory, subject to the rule above: it must stop storing
the Go pointer when the C function returns.
These rules are checked dynamically at runtime. The checking is
controlled by the cgocheck setting of the GODEBUG environment
variable. The default setting is GODEBUG=cgocheck=1, which implements
reasonably cheap dynamic checks. These checks may be disabled
entirely using GODEBUG=cgocheck=0. Complete checking of pointer
handling, at some cost in run time, is available via GODEBUG=cgocheck=2.
It is possible to defeat this enforcement by using the unsafe package,
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.
Using cgo directly
Usage:
@ -391,17 +450,13 @@ the translation process.
Translating Go
[The rest of this comment refers to 6g, the Go compiler that is part
of the amd64 port of the gc Go toolchain. Everything here applies to
another architecture's compilers as well.]
Given the input Go files x.go and y.go, cgo generates these source
files:
x.cgo1.go # for 6g
y.cgo1.go # for 6g
_cgo_gotypes.go # for 6g
_cgo_import.go # for 6g (if -dynout _cgo_import.go)
x.cgo1.go # for gc (cmd/compile)
y.cgo1.go # for gc
_cgo_gotypes.go # for gc
_cgo_import.go # for gc (if -dynout _cgo_import.go)
x.cgo2.c # for gcc
y.cgo2.c # for gcc
_cgo_defun.c # for gcc (if -gccgo)
@ -464,7 +519,7 @@ Linking
Once the _cgo_export.c and *.cgo2.c files have been compiled with gcc,
they need to be linked into the final binary, along with the libraries
they might depend on (in the case of puts, stdio). 6l has been
they might depend on (in the case of puts, stdio). cmd/link has been
extended to understand basic ELF files, but it does not understand ELF
in the full complexity that modern C libraries embrace, so it cannot
in general generate direct references to the system libraries.
@ -495,23 +550,23 @@ _cgo_import.go, which looks like:
//go:cgo_import_dynamic _ _ "libc.so.6"
In the end, the compiled Go package, which will eventually be
presented to 6l as part of a larger program, contains:
presented to cmd/link as part of a larger program, contains:
_go_.6 # 6g-compiled object for _cgo_gotypes.go, _cgo_import.go, *.cgo1.go
_go_.o # gc-compiled object for _cgo_gotypes.go, _cgo_import.go, *.cgo1.go
_all.o # gcc-compiled object for _cgo_export.c, *.cgo2.c
The final program will be a dynamic executable, so that 6l can avoid
The final program will be a dynamic executable, so that cmd/link can avoid
needing to process arbitrary .o files. It only needs to process the .o
files generated from C files that cgo writes, and those are much more
limited in the ELF or other features that they use.
In essence, the _cgo_import.6 file includes the extra linking
directives that 6l is not sophisticated enough to derive from _all.o
In essence, the _cgo_import.o file includes the extra linking
directives that cmd/link is not sophisticated enough to derive from _all.o
on its own. Similarly, the _all.o uses dynamic references to real
system object code because 6l is not sophisticated enough to process
system object code because cmd/link is not sophisticated enough to process
the real code.
The main benefits of this system are that 6l remains relatively simple
The main benefits of this system are that cmd/link remains relatively simple
(it does not need to implement a complete ELF and Mach-O linker) and
that gcc is not needed after the package is compiled. For example,
package net uses cgo for access to name resolution functions provided
@ -540,17 +595,17 @@ system calls.
Internal and External Linking
The text above describes "internal" linking, in which 6l parses and
The text above describes "internal" linking, in which cmd/link parses and
links host object files (ELF, Mach-O, PE, and so on) into the final
executable itself. Keeping 6l simple means we cannot possibly
executable itself. Keeping cmd/link simple means we cannot possibly
implement the full semantics of the host linker, so the kinds of
objects that can be linked directly into the binary is limited (other
code can only be used as a dynamic library). On the other hand, when
using internal linking, 6l can generate Go binaries by itself.
using internal linking, cmd/link can generate Go binaries by itself.
In order to allow linking arbitrary object files without requiring
dynamic libraries, cgo supports an "external" linking mode too. In
external linking mode, 6l does not process any host object files.
external linking mode, cmd/link does not process any host object files.
Instead, it collects all the Go code and writes a single go.o object
file containing it. Then it invokes the host linker (usually gcc) to
combine the go.o object file and any supporting non-Go code into a
@ -582,8 +637,8 @@ to be made when linking the final binary.
Linking Directives
In either linking mode, package-specific directives must be passed
through to 6l. These are communicated by writing //go: directives in a
Go source file compiled by 6g. The directives are copied into the .6
through to cmd/link. These are communicated by writing //go: directives in a
Go source file compiled by gc. The directives are copied into the .o
object file and then processed by the linker.
The directives are:
@ -672,7 +727,7 @@ Example
As a simple example, consider a package that uses cgo to call C.sin.
The following code will be generated by cgo:
// compiled by 6g
// compiled by gc
//go:cgo_ldflag "-lm"
@ -708,7 +763,7 @@ Otherwise the link will be an internal one.
The linking directives are used according to the kind of final link
used.
In internal mode, 6l itself processes all the host object files, in
In internal mode, cmd/link itself processes all the host object files, in
particular foo.cgo2.o. To do so, it uses the cgo_import_dynamic and
cgo_dynamic_linker directives to learn that the otherwise undefined
reference to sin in foo.cgo2.o should be rewritten to refer to the
@ -716,56 +771,56 @@ symbol sin with version GLIBC_2.2.5 from the dynamic library
"libm.so.6", and the binary should request "/lib/ld-linux.so.2" as its
runtime dynamic linker.
In external mode, 6l does not process any host object files, in
particular foo.cgo2.o. It links together the 6g-generated object
In external mode, cmd/link does not process any host object files, in
particular foo.cgo2.o. It links together the gc-generated object
files, along with any other Go code, into a go.o file. While doing
that, 6l will discover that there is no definition for
_cgo_gcc_Cfunc_sin, referred to by the 6g-compiled source file. This
is okay, because 6l also processes the cgo_import_static directive and
that, cmd/link will discover that there is no definition for
_cgo_gcc_Cfunc_sin, referred to by the gc-compiled source file. This
is okay, because cmd/link also processes the cgo_import_static directive and
knows that _cgo_gcc_Cfunc_sin is expected to be supplied by a host
object file, so 6l does not treat the missing symbol as an error when
object file, so cmd/link does not treat the missing symbol as an error when
creating go.o. Indeed, the definition for _cgo_gcc_Cfunc_sin will be
provided to the host linker by foo2.cgo.o, which in turn will need the
symbol 'sin'. 6l also processes the cgo_ldflag directives, so that it
symbol 'sin'. cmd/link also processes the cgo_ldflag directives, so that it
knows that the eventual host link command must include the -lm
argument, so that the host linker will be able to find 'sin' in the
math library.
6l Command Line Interface
cmd/link Command Line Interface
The go command and any other Go-aware build systems invoke 6l
to link a collection of packages into a single binary. By default, 6l will
The go command and any other Go-aware build systems invoke cmd/link
to link a collection of packages into a single binary. By default, cmd/link will
present the same interface it does today:
6l main.a
cmd/link main.a
produces a file named 6.out, even if 6l does so by invoking the host
produces a file named a.out, even if cmd/link does so by invoking the host
linker in external linking mode.
By default, 6l will decide the linking mode as follows: if the only
By default, cmd/link will decide the linking mode as follows: if the only
packages using cgo are those on a whitelist of standard library
packages (net, os/user, runtime/cgo), 6l will use internal linking
mode. Otherwise, there are non-standard cgo packages involved, and 6l
packages (net, os/user, runtime/cgo), cmd/link will use internal linking
mode. Otherwise, there are non-standard cgo packages involved, and cmd/link
will use external linking mode. The first rule means that a build of
the godoc binary, which uses net but no other cgo, can run without
needing gcc available. The second rule means that a build of a
cgo-wrapped library like sqlite3 can generate a standalone executable
instead of needing to refer to a dynamic library. The specific choice
can be overridden using a command line flag: 6l -linkmode=internal or
6l -linkmode=external.
can be overridden using a command line flag: cmd/link -linkmode=internal or
cmd/link -linkmode=external.
In an external link, 6l will create a temporary directory, write any
In an external link, cmd/link will create a temporary directory, write any
host object files found in package archives to that directory (renamed
to avoid conflicts), write the go.o file to that directory, and invoke
the host linker. The default value for the host linker is $CC, split
into fields, or else "gcc". The specific host linker command line can
be overridden using command line flags: 6l -extld=clang
be overridden using command line flags: cmd/link -extld=clang
-extldflags='-ggdb -O3'. If any package in a build includes a .cc or
other file compiled by the C++ compiler, the go tool will use the
-extld option to set the host linker to the C++ compiler.
These defaults mean that Go-aware build systems can ignore the linking
changes and keep running plain '6l' and get reasonable results, but
changes and keep running plain 'cmd/link' and get reasonable results, but
they can also control the linking details if desired.
*/

View File

@ -38,8 +38,8 @@ var nameToC = map[string]string{
"ulong": "unsigned long",
"longlong": "long long",
"ulonglong": "unsigned long long",
"complexfloat": "float complex",
"complexdouble": "double complex",
"complexfloat": "float _Complex",
"complexdouble": "double _Complex",
}
// cname returns the C name to use for C.s.
@ -167,6 +167,7 @@ func (p *Package) Translate(f *File) {
if len(needType) > 0 {
p.loadDWARF(f, needType)
}
p.rewriteCalls(f)
p.rewriteRef(f)
}
@ -575,6 +576,331 @@ func (p *Package) mangleName(n *Name) {
n.Mangle = prefix + n.Kind + "_" + n.Go
}
// rewriteCalls rewrites all calls that pass pointers to check that
// they follow the rules for passing pointers between Go and C.
func (p *Package) rewriteCalls(f *File) {
for _, call := range f.Calls {
// This is a call to C.xxx; set goname to "xxx".
goname := call.Fun.(*ast.SelectorExpr).Sel.Name
if goname == "malloc" {
continue
}
name := f.Name[goname]
if name.Kind != "func" {
// Probably a type conversion.
continue
}
p.rewriteCall(f, call, name)
}
}
// rewriteCall rewrites one call to add pointer checks. We replace
// each pointer argument x with _cgoCheckPointer(x).(T).
func (p *Package) rewriteCall(f *File, call *ast.CallExpr, name *Name) {
for i, param := range name.FuncType.Params {
if len(call.Args) <= i {
// Avoid a crash; this will be caught when the
// generated file is compiled.
return
}
// An untyped nil does not need a pointer check, and
// when _cgoCheckPointer returns the untyped nil the
// type assertion we are going to insert will fail.
// Easier to just skip nil arguments.
// TODO: Note that this fails if nil is shadowed.
if id, ok := call.Args[i].(*ast.Ident); ok && id.Name == "nil" {
continue
}
if !p.needsPointerCheck(f, param.Go) {
continue
}
c := &ast.CallExpr{
Fun: ast.NewIdent("_cgoCheckPointer"),
Args: []ast.Expr{
call.Args[i],
},
}
// Add optional additional arguments for an address
// expression.
c.Args = p.checkAddrArgs(f, c.Args, call.Args[i])
// _cgoCheckPointer returns interface{}.
// We need to type assert that to the type we want.
// If the Go version of this C type uses
// unsafe.Pointer, we can't use a type assertion,
// because the Go file might not import unsafe.
// Instead we use a local variant of _cgoCheckPointer.
var arg ast.Expr
if n := p.unsafeCheckPointerName(param.Go); n != "" {
c.Fun = ast.NewIdent(n)
arg = c
} else {
// In order for the type assertion to succeed,
// we need it to match the actual type of the
// argument. The only type we have is the
// type of the function parameter. We know
// that the argument type must be assignable
// to the function parameter type, or the code
// would not compile, but there is nothing
// requiring that the types be exactly the
// same. Add a type conversion to the
// argument so that the type assertion will
// succeed.
c.Args[0] = &ast.CallExpr{
Fun: param.Go,
Args: []ast.Expr{
c.Args[0],
},
}
arg = &ast.TypeAssertExpr{
X: c,
Type: param.Go,
}
}
call.Args[i] = arg
}
}
// needsPointerCheck returns whether the type t needs a pointer check.
// This is true if t is a pointer and if the value to which it points
// might contain a pointer.
func (p *Package) needsPointerCheck(f *File, t ast.Expr) bool {
return p.hasPointer(f, t, true)
}
// hasPointer is used by needsPointerCheck. If top is true it returns
// whether t is or contains a pointer that might point to a pointer.
// If top is false it returns whether t is or contains a pointer.
// f may be nil.
func (p *Package) hasPointer(f *File, t ast.Expr, top bool) bool {
switch t := t.(type) {
case *ast.ArrayType:
if t.Len == nil {
if !top {
return true
}
return p.hasPointer(f, t.Elt, false)
}
return p.hasPointer(f, t.Elt, top)
case *ast.StructType:
for _, field := range t.Fields.List {
if p.hasPointer(f, field.Type, top) {
return true
}
}
return false
case *ast.StarExpr: // Pointer type.
if !top {
return true
}
return p.hasPointer(f, t.X, false)
case *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType:
return true
case *ast.Ident:
// TODO: Handle types defined within function.
for _, d := range p.Decl {
gd, ok := d.(*ast.GenDecl)
if !ok || gd.Tok != token.TYPE {
continue
}
for _, spec := range gd.Specs {
ts, ok := spec.(*ast.TypeSpec)
if !ok {
continue
}
if ts.Name.Name == t.Name {
return p.hasPointer(f, ts.Type, top)
}
}
}
if def := typedef[t.Name]; def != nil {
return p.hasPointer(f, def.Go, top)
}
if t.Name == "string" {
return !top
}
if t.Name == "error" {
return true
}
if goTypes[t.Name] != nil {
return false
}
// We can't figure out the type. Conservative
// approach is to assume it has a pointer.
return true
case *ast.SelectorExpr:
if l, ok := t.X.(*ast.Ident); !ok || l.Name != "C" {
// Type defined in a different package.
// Conservative approach is to assume it has a
// pointer.
return true
}
if f == nil {
// Conservative approach: assume pointer.
return true
}
name := f.Name[t.Sel.Name]
if name != nil && name.Kind == "type" && name.Type != nil && name.Type.Go != nil {
return p.hasPointer(f, name.Type.Go, top)
}
// We can't figure out the type. Conservative
// approach is to assume it has a pointer.
return true
default:
error_(t.Pos(), "could not understand type %s", gofmt(t))
return true
}
}
// checkAddrArgs tries to add arguments to the call of
// _cgoCheckPointer when the argument is an address expression. We
// pass true to mean that the argument is an address operation of
// something other than a slice index, which means that it's only
// necessary to check the specific element pointed to, not the entire
// object. This is for &s.f, where f is a field in a struct. We can
// pass a slice or array, meaning that we should check the entire
// slice or array but need not check any other part of the object.
// This is for &s.a[i], where we need to check all of a. However, we
// only pass the slice or array if we can refer to it without side
// effects.
func (p *Package) checkAddrArgs(f *File, args []ast.Expr, x ast.Expr) []ast.Expr {
// Strip type conversions.
for {
c, ok := x.(*ast.CallExpr)
if !ok || len(c.Args) != 1 || !p.isType(c.Fun) {
break
}
x = c.Args[0]
}
u, ok := x.(*ast.UnaryExpr)
if !ok || u.Op != token.AND {
return args
}
index, ok := u.X.(*ast.IndexExpr)
if !ok {
// This is the address of something that is not an
// index expression. We only need to examine the
// single value to which it points.
// TODO: what if true is shadowed?
return append(args, ast.NewIdent("true"))
}
if !p.hasSideEffects(f, index.X) {
// Examine the entire slice.
return append(args, index.X)
}
// Treat the pointer as unknown.
return args
}
// hasSideEffects returns whether the expression x has any side
// effects. x is an expression, not a statement, so the only side
// effect is a function call.
func (p *Package) hasSideEffects(f *File, x ast.Expr) bool {
found := false
f.walk(x, "expr",
func(f *File, x interface{}, context string) {
switch x.(type) {
case *ast.CallExpr:
found = true
}
})
return found
}
// isType returns whether the expression is definitely a type.
// This is conservative--it returns false for an unknown identifier.
func (p *Package) isType(t ast.Expr) bool {
switch t := t.(type) {
case *ast.SelectorExpr:
if t.Sel.Name != "Pointer" {
return false
}
id, ok := t.X.(*ast.Ident)
if !ok {
return false
}
return id.Name == "unsafe"
case *ast.Ident:
// TODO: This ignores shadowing.
switch t.Name {
case "unsafe.Pointer", "bool", "byte",
"complex64", "complex128",
"error",
"float32", "float64",
"int", "int8", "int16", "int32", "int64",
"rune", "string",
"uint", "uint8", "uint16", "uint32", "uint64", "uintptr":
return true
}
case *ast.StarExpr:
return p.isType(t.X)
case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType,
*ast.MapType, *ast.ChanType:
return true
}
return false
}
// unsafeCheckPointerName is given the Go version of a C type. If the
// type uses unsafe.Pointer, we arrange to build a version of
// _cgoCheckPointer that returns that type. This avoids using a type
// assertion to unsafe.Pointer in our copy of user code. We return
// the name of the _cgoCheckPointer function we are going to build, or
// the empty string if the type does not use unsafe.Pointer.
func (p *Package) unsafeCheckPointerName(t ast.Expr) string {
if !p.hasUnsafePointer(t) {
return ""
}
var buf bytes.Buffer
conf.Fprint(&buf, fset, t)
s := buf.String()
for i, t := range p.CgoChecks {
if s == t {
return p.unsafeCheckPointerNameIndex(i)
}
}
p.CgoChecks = append(p.CgoChecks, s)
return p.unsafeCheckPointerNameIndex(len(p.CgoChecks) - 1)
}
// hasUnsafePointer returns whether the Go type t uses unsafe.Pointer.
// t is the Go version of a C type, so we don't need to handle every case.
// We only care about direct references, not references via typedefs.
func (p *Package) hasUnsafePointer(t ast.Expr) bool {
switch t := t.(type) {
case *ast.Ident:
// We don't see a SelectorExpr for unsafe.Pointer;
// this is created by code in this file.
return t.Name == "unsafe.Pointer"
case *ast.ArrayType:
return p.hasUnsafePointer(t.Elt)
case *ast.StructType:
for _, f := range t.Fields.List {
if p.hasUnsafePointer(f.Type) {
return true
}
}
case *ast.StarExpr: // Pointer type.
return p.hasUnsafePointer(t.X)
}
return false
}
// unsafeCheckPointerNameIndex returns the name to use for a
// _cgoCheckPointer variant based on the index in the CgoChecks slice.
func (p *Package) unsafeCheckPointerNameIndex(i int) string {
return fmt.Sprintf("_cgoCheckPointer%d", i)
}
// rewriteRef rewrites all the C.xxx references in f.AST to refer to the
// Go equivalents, now that we have figured out the meaning of all
// the xxx. In *godefs mode, rewriteRef replaces the names
@ -612,6 +938,10 @@ func (p *Package) rewriteRef(f *File) {
if r.Name.Kind != "func" {
if r.Name.Kind == "type" {
r.Context = "type"
if r.Name.Type == nil {
error_(r.Pos(), "invalid conversion to C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)
break
}
expr = r.Name.Type.Go
break
}
@ -663,6 +993,10 @@ func (p *Package) rewriteRef(f *File) {
}
} else if r.Name.Kind == "type" {
// Okay - might be new(T)
if r.Name.Type == nil {
error_(r.Pos(), "expression C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)
break
}
expr = r.Name.Type.Go
} else if r.Name.Kind == "var" {
expr = &ast.StarExpr{Star: (*r.Expr).Pos(), X: expr}
@ -1028,12 +1362,12 @@ var dwarfToName = map[string]string{
"long unsigned int": "ulong",
"unsigned int": "uint",
"short unsigned int": "ushort",
"unsigned short": "ushort", // Used by Clang; issue 13129.
"short int": "short",
"long long int": "longlong",
"long long unsigned int": "ulonglong",
"signed char": "schar",
"float complex": "complexfloat",
"double complex": "complexdouble",
"unsigned char": "uchar",
}
const signedDelta = 64
@ -1224,6 +1558,11 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
t.Go = c.int32
case 8:
t.Go = c.int64
case 16:
t.Go = &ast.ArrayType{
Len: c.intExpr(t.Size),
Elt: c.uint8,
}
}
if t.Align = t.Size; t.Align >= c.ptrSize {
t.Align = c.ptrSize
@ -1381,6 +1720,11 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
t.Go = c.uint32
case 8:
t.Go = c.uint64
case 16:
t.Go = &ast.ArrayType{
Len: c.intExpr(t.Size),
Elt: c.uint8,
}
}
if t.Align = t.Size; t.Align >= c.ptrSize {
t.Align = c.ptrSize
@ -1393,7 +1737,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
}
switch dtype.(type) {
case *dwarf.AddrType, *dwarf.BoolType, *dwarf.CharType, *dwarf.IntType, *dwarf.FloatType, *dwarf.UcharType, *dwarf.UintType:
case *dwarf.AddrType, *dwarf.BoolType, *dwarf.CharType, *dwarf.ComplexType, *dwarf.IntType, *dwarf.FloatType, *dwarf.UcharType, *dwarf.UintType:
s := dtype.Common().Name
if s != "" {
if ss, ok := dwarfToName[s]; ok {

View File

@ -11,6 +11,7 @@ import (
"go/printer"
"go/token"
"os"
"path/filepath"
"strings"
)
@ -19,7 +20,7 @@ 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, "// %s\n", strings.Join(os.Args, " "))
fmt.Fprintf(&buf, "// %s %s\n", filepath.Base(os.Args[0]), strings.Join(os.Args[1:], " "))
fmt.Fprintf(&buf, "\n")
override := make(map[string]string)

View File

@ -42,6 +42,7 @@ type Package struct {
GoFiles []string // list of Go files
GccFiles []string // list of gcc output files
Preamble string // collected preamble for _cgo_export.h
CgoChecks []string // see unsafeCheckPointerName
}
// A File collects information about a single Go input file.
@ -51,6 +52,7 @@ type File struct {
Package string // Package name
Preamble string // C preamble (doc comment on import "C")
Ref []*Ref // all references to C.xxx in AST
Calls []*ast.CallExpr // all calls to C.xxx in AST
ExpFunc []*ExpFunc // exported functions for this file
Name map[string]*Name // map from Go name to Name
}
@ -132,43 +134,47 @@ func usage() {
}
var ptrSizeMap = map[string]int64{
"386": 4,
"alpha": 8,
"amd64": 8,
"arm": 4,
"arm64": 8,
"m68k": 4,
"mipso32": 4,
"mipsn32": 4,
"mipso64": 8,
"mipsn64": 8,
"ppc": 4,
"ppc64": 8,
"ppc64le": 8,
"s390": 4,
"s390x": 8,
"sparc": 4,
"sparc64": 8,
"386": 4,
"alpha": 8,
"amd64": 8,
"arm": 4,
"arm64": 8,
"m68k": 4,
"mipso32": 4,
"mipsn32": 4,
"mipso64": 8,
"mipsn64": 8,
"mips64": 8,
"mips64le": 8,
"ppc": 4,
"ppc64": 8,
"ppc64le": 8,
"s390": 4,
"s390x": 8,
"sparc": 4,
"sparc64": 8,
}
var intSizeMap = map[string]int64{
"386": 4,
"alpha": 8,
"amd64": 8,
"arm": 4,
"arm64": 8,
"m68k": 4,
"mipso32": 4,
"mipsn32": 4,
"mipso64": 8,
"mipsn64": 8,
"ppc": 4,
"ppc64": 8,
"ppc64le": 8,
"s390": 4,
"s390x": 8,
"sparc": 4,
"sparc64": 8,
"386": 4,
"alpha": 8,
"amd64": 8,
"arm": 4,
"arm64": 8,
"m68k": 4,
"mipso32": 4,
"mipsn32": 4,
"mipso64": 8,
"mipsn64": 8,
"mips64": 8,
"mips64le": 8,
"ppc": 4,
"ppc64": 8,
"ppc64le": 8,
"s390": 4,
"s390x": 8,
"sparc": 4,
"sparc64": 8,
}
var cPrefix string
@ -297,11 +303,7 @@ func main() {
if nerrors > 0 {
os.Exit(2)
}
pkg := f.Package
if dir := os.Getenv("CGOPKGPATH"); dir != "" {
pkg = filepath.Join(dir, pkg)
}
p.PackagePath = pkg
p.PackagePath = f.Package
p.Record(f)
if *godefs {
os.Stdout.WriteString(p.godefs(f, input))

View File

@ -103,11 +103,19 @@ func (p *Package) writeDefs() {
}
if *gccgo {
fmt.Fprint(fgo2, gccgoGoProlog)
fmt.Fprint(fc, p.cPrologGccgo())
} else {
fmt.Fprint(fgo2, goProlog)
}
for i, t := range p.CgoChecks {
n := p.unsafeCheckPointerNameIndex(i)
fmt.Fprintf(fgo2, "\nfunc %s(p interface{}, args ...interface{}) %s {\n", n, t)
fmt.Fprintf(fgo2, "\treturn _cgoCheckPointer(p, args...).(%s)\n", t)
fmt.Fprintf(fgo2, "}\n")
}
gccgoSymbolPrefix := p.gccgoSymbolPrefix()
cVars := make(map[string]bool)
@ -693,7 +701,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
}
fntype := fn.Type
forFieldList(fntype.Params,
func(i int, atype ast.Expr) {
func(i int, aname string, atype ast.Expr) {
t := p.cgoType(atype)
if off%t.Align != 0 {
pad := t.Align - off%t.Align
@ -711,7 +719,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
npad++
}
forFieldList(fntype.Results,
func(i int, atype ast.Expr) {
func(i int, aname string, atype ast.Expr) {
t := p.cgoType(atype)
if off%t.Align != 0 {
pad := t.Align - off%t.Align
@ -744,8 +752,12 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
fmt.Fprintf(fgcch, "\n/* Return type for %s */\n", exp.ExpName)
fmt.Fprintf(fgcch, "struct %s_return {\n", exp.ExpName)
forFieldList(fntype.Results,
func(i int, atype ast.Expr) {
fmt.Fprintf(fgcch, "\t%s r%d;\n", p.cgoType(atype).C, i)
func(i int, aname string, atype ast.Expr) {
fmt.Fprintf(fgcch, "\t%s r%d;", p.cgoType(atype).C, i)
if len(aname) > 0 {
fmt.Fprintf(fgcch, " /* %s */", aname)
}
fmt.Fprint(fgcch, "\n")
})
fmt.Fprintf(fgcch, "};\n")
gccResult = "struct " + exp.ExpName + "_return"
@ -758,7 +770,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
s += " recv"
}
forFieldList(fntype.Params,
func(i int, atype ast.Expr) {
func(i int, aname string, atype ast.Expr) {
if i > 0 || fn.Recv != nil {
s += ", "
}
@ -783,7 +795,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
fmt.Fprintf(fgcc, "\ta.recv = recv;\n")
}
forFieldList(fntype.Params,
func(i int, atype ast.Expr) {
func(i int, aname string, atype ast.Expr) {
fmt.Fprintf(fgcc, "\ta.p%d = p%d;\n", i, i)
})
fmt.Fprintf(fgcc, "\tcrosscall2(_cgoexp%s_%s, &a, %d);\n", cPrefix, exp.ExpName, off)
@ -792,7 +804,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
fmt.Fprintf(fgcc, "\treturn a.r0;\n")
} else {
forFieldList(fntype.Results,
func(i int, atype ast.Expr) {
func(i int, aname string, atype ast.Expr) {
fmt.Fprintf(fgcc, "\tr.r%d = a.r%d;\n", i, i)
})
fmt.Fprintf(fgcc, "\treturn r;\n")
@ -800,17 +812,18 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
}
fmt.Fprintf(fgcc, "}\n")
// Build the wrapper function compiled by gc.
goname := exp.Func.Name.Name
// Build the wrapper function compiled by cmd/compile.
goname := "_cgoexpwrap" + cPrefix + "_"
if fn.Recv != nil {
goname = "_cgoexpwrap" + cPrefix + "_" + fn.Recv.List[0].Names[0].Name + "_" + goname
goname += fn.Recv.List[0].Names[0].Name + "_"
}
fmt.Fprintf(fgo2, "//go:cgo_export_dynamic %s\n", goname)
goname += exp.Func.Name.Name
fmt.Fprintf(fgo2, "//go:cgo_export_dynamic %s\n", exp.ExpName)
fmt.Fprintf(fgo2, "//go:linkname _cgoexp%s_%s _cgoexp%s_%s\n", cPrefix, exp.ExpName, cPrefix, exp.ExpName)
fmt.Fprintf(fgo2, "//go:cgo_export_static _cgoexp%s_%s\n", cPrefix, exp.ExpName)
fmt.Fprintf(fgo2, "//go:nosplit\n") // no split stack, so no use of m or g
fmt.Fprintf(fgo2, "//go:norace\n") // must not have race detector calls inserted
fmt.Fprintf(fgo2, "func _cgoexp%s_%s(a unsafe.Pointer, n int32) {", cPrefix, exp.ExpName)
fmt.Fprintf(fgo2, "func _cgoexp%s_%s(a unsafe.Pointer, n int32) {\n", cPrefix, exp.ExpName)
fmt.Fprintf(fgo2, "\tfn := %s\n", goname)
// The indirect here is converting from a Go function pointer to a C function pointer.
fmt.Fprintf(fgo2, "\t_cgo_runtime_cgocallback(**(**unsafe.Pointer)(unsafe.Pointer(&fn)), a, uintptr(n));\n")
@ -818,44 +831,75 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
fmt.Fprintf(fm, "int _cgoexp%s_%s;\n", cPrefix, exp.ExpName)
// Calling a function with a receiver from C requires
// a Go wrapper function.
// This code uses printer.Fprint, not conf.Fprint,
// because we don't want //line comments in the middle
// of the function types.
fmt.Fprintf(fgo2, "\n")
fmt.Fprintf(fgo2, "func %s(", goname)
comma := false
if fn.Recv != nil {
fmt.Fprintf(fgo2, "func %s(recv ", goname)
conf.Fprint(fgo2, fset, fn.Recv.List[0].Type)
forFieldList(fntype.Params,
func(i int, atype ast.Expr) {
fmt.Fprintf(fgo2, ", p%d ", i)
conf.Fprint(fgo2, fset, atype)
})
fmt.Fprintf(fgo2, ")")
if gccResult != "void" {
fmt.Fprint(fgo2, " (")
forFieldList(fntype.Results,
func(i int, atype ast.Expr) {
if i > 0 {
fmt.Fprint(fgo2, ", ")
}
conf.Fprint(fgo2, fset, atype)
})
fmt.Fprint(fgo2, ")")
}
fmt.Fprint(fgo2, " {\n")
fmt.Fprint(fgo2, "\t")
if gccResult != "void" {
fmt.Fprint(fgo2, "return ")
}
fmt.Fprintf(fgo2, "recv.%s(", exp.Func.Name)
forFieldList(fntype.Params,
func(i int, atype ast.Expr) {
fmt.Fprintf(fgo2, "recv ")
printer.Fprint(fgo2, fset, fn.Recv.List[0].Type)
comma = true
}
forFieldList(fntype.Params,
func(i int, aname string, atype ast.Expr) {
if comma {
fmt.Fprintf(fgo2, ", ")
}
fmt.Fprintf(fgo2, "p%d ", i)
printer.Fprint(fgo2, fset, atype)
comma = true
})
fmt.Fprintf(fgo2, ")")
if gccResult != "void" {
fmt.Fprint(fgo2, " (")
forFieldList(fntype.Results,
func(i int, aname string, atype ast.Expr) {
if i > 0 {
fmt.Fprint(fgo2, ", ")
}
fmt.Fprintf(fgo2, "p%d", i)
fmt.Fprintf(fgo2, "r%d ", i)
printer.Fprint(fgo2, fset, atype)
})
fmt.Fprint(fgo2, ")\n")
fmt.Fprint(fgo2, "}\n")
fmt.Fprint(fgo2, ")")
}
fmt.Fprint(fgo2, " {\n")
if gccResult == "void" {
fmt.Fprint(fgo2, "\t")
} else {
// Verify that any results don't contain any
// Go pointers.
addedDefer := false
forFieldList(fntype.Results,
func(i int, aname string, atype ast.Expr) {
if !p.hasPointer(nil, atype, false) {
return
}
if !addedDefer {
fmt.Fprint(fgo2, "\tdefer func() {\n")
addedDefer = true
}
fmt.Fprintf(fgo2, "\t\t_cgoCheckResult(r%d)\n", i)
})
if addedDefer {
fmt.Fprint(fgo2, "\t}()\n")
}
fmt.Fprint(fgo2, "\treturn ")
}
if fn.Recv != nil {
fmt.Fprintf(fgo2, "recv.")
}
fmt.Fprintf(fgo2, "%s(", exp.Func.Name)
forFieldList(fntype.Params,
func(i int, aname string, atype ast.Expr) {
if i > 0 {
fmt.Fprint(fgo2, ", ")
}
fmt.Fprintf(fgo2, "p%d", i)
})
fmt.Fprint(fgo2, ")\n")
fmt.Fprint(fgo2, "}\n")
}
fmt.Fprintf(fgcch, "%s", gccExportHeaderEpilog)
@ -879,13 +923,13 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) {
cdeclBuf := new(bytes.Buffer)
resultCount := 0
forFieldList(fntype.Results,
func(i int, atype ast.Expr) { resultCount++ })
func(i int, aname string, atype ast.Expr) { resultCount++ })
switch resultCount {
case 0:
fmt.Fprintf(cdeclBuf, "void")
case 1:
forFieldList(fntype.Results,
func(i int, atype ast.Expr) {
func(i int, aname string, atype ast.Expr) {
t := p.cgoType(atype)
fmt.Fprintf(cdeclBuf, "%s", t.C)
})
@ -894,9 +938,13 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) {
fmt.Fprintf(fgcch, "\n/* Return type for %s */\n", exp.ExpName)
fmt.Fprintf(fgcch, "struct %s_result {\n", exp.ExpName)
forFieldList(fntype.Results,
func(i int, atype ast.Expr) {
func(i int, aname string, atype ast.Expr) {
t := p.cgoType(atype)
fmt.Fprintf(fgcch, "\t%s r%d;\n", t.C, i)
fmt.Fprintf(fgcch, "\t%s r%d;", t.C, i)
if len(aname) > 0 {
fmt.Fprintf(fgcch, " /* %s */", aname)
}
fmt.Fprint(fgcch, "\n")
})
fmt.Fprintf(fgcch, "};\n")
fmt.Fprintf(cdeclBuf, "struct %s_result", exp.ExpName)
@ -911,7 +959,7 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) {
}
// Function parameters.
forFieldList(fntype.Params,
func(i int, atype ast.Expr) {
func(i int, aname string, atype ast.Expr) {
if i > 0 || fn.Recv != nil {
fmt.Fprintf(cdeclBuf, ", ")
}
@ -925,23 +973,15 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) {
fmt.Fprintf(fgcch, "\n%s", exp.Doc)
}
fmt.Fprintf(fgcch, "extern %s %s %s;\n", cRet, exp.ExpName, cParams)
// We need to use a name that will be exported by the
// Go code; otherwise gccgo will make it static and we
// will not be able to link against it from the C
// code.
goName := "Cgoexp_" + exp.ExpName
fmt.Fprintf(fgcch, `extern %s %s %s __asm__("%s.%s");`, cRet, goName, cParams, gccgoSymbolPrefix, goName)
fmt.Fprint(fgcch, "\n")
// Use a #define so that the C code that includes
// cgo_export.h will be able to refer to the Go
// function using the expected name.
fmt.Fprintf(fgcch, "#define %s %s\n", exp.ExpName, goName)
// Use a #undef in _cgo_export.c so that we ignore the
// #define from cgo_export.h, since here we are
// defining the real function.
fmt.Fprintf(fgcc, "#undef %s\n", exp.ExpName)
fmt.Fprintf(fgcc, `extern %s %s %s __asm__("%s.%s");`, cRet, goName, cParams, gccgoSymbolPrefix, goName)
fmt.Fprint(fgcc, "\n")
fmt.Fprint(fgcc, "\n")
fmt.Fprintf(fgcc, "%s %s %s {\n", cRet, exp.ExpName, cParams)
@ -956,7 +996,7 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) {
fmt.Fprint(fgcc, "recv")
}
forFieldList(fntype.Params,
func(i int, atype ast.Expr) {
func(i int, aname string, atype ast.Expr) {
if i > 0 || fn.Recv != nil {
fmt.Fprintf(fgcc, ", ")
}
@ -982,7 +1022,7 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) {
printer.Fprint(fgo2, fset, fn.Recv.List[0].Type)
}
forFieldList(fntype.Params,
func(i int, atype ast.Expr) {
func(i int, aname string, atype ast.Expr) {
if i > 0 || fn.Recv != nil {
fmt.Fprintf(fgo2, ", ")
}
@ -993,7 +1033,7 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) {
if resultCount > 0 {
fmt.Fprintf(fgo2, " (")
forFieldList(fntype.Results,
func(i int, atype ast.Expr) {
func(i int, aname string, atype ast.Expr) {
if i > 0 {
fmt.Fprint(fgo2, ", ")
}
@ -1013,7 +1053,7 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) {
}
fmt.Fprintf(fgo2, "%s(", exp.Func.Name)
forFieldList(fntype.Params,
func(i int, atype ast.Expr) {
func(i int, aname string, atype ast.Expr) {
if i > 0 {
fmt.Fprint(fgo2, ", ")
}
@ -1071,19 +1111,19 @@ func (p *Package) gccgoSymbolPrefix() string {
}
// Call a function for each entry in an ast.FieldList, passing the
// index into the list and the type.
func forFieldList(fl *ast.FieldList, fn func(int, ast.Expr)) {
// index into the list, the name if any, and the type.
func forFieldList(fl *ast.FieldList, fn func(int, string, ast.Expr)) {
if fl == nil {
return
}
i := 0
for _, r := range fl.List {
if r.Names == nil {
fn(i, r.Type)
fn(i, "", r.Type)
i++
} else {
for range r.Names {
fn(i, r.Type)
for _, n := range r.Names {
fn(i, n.Name, r.Type)
i++
}
}
@ -1193,9 +1233,11 @@ func (p *Package) cgoType(e ast.Expr) *Type {
}
const gccProlog = `
// Usual nonsense: if x and y are not equal, the type will be invalid
// (have a negative array count) and an inscrutable error will come
// out of the compiler and hopefully mention "name".
/*
If x and y are not equal, the type will be invalid
(have a negative array count) and an inscrutable error will come
out of the compiler and hopefully mention "name".
*/
#define __cgo_compile_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2+1];
// Check at compile time that the sizes we use match our expectations.
@ -1239,6 +1281,18 @@ func _cgo_runtime_cmalloc(uintptr) unsafe.Pointer
//go:linkname _cgo_runtime_cgocallback runtime.cgocallback
func _cgo_runtime_cgocallback(unsafe.Pointer, unsafe.Pointer, uintptr)
//go:linkname _cgoCheckPointer runtime.cgoCheckPointer
func _cgoCheckPointer(interface{}, ...interface{}) interface{}
//go:linkname _cgoCheckResult runtime.cgoCheckResult
func _cgoCheckResult(interface{})
`
const gccgoGoProlog = `
func _cgoCheckPointer(interface{}, ...interface{}) interface{}
func _cgoCheckResult(interface{})
`
const goStringDef = `
@ -1293,7 +1347,8 @@ var builtinDefs = map[string]string{
}
func (p *Package) cPrologGccgo() string {
return strings.Replace(cPrologGccgo, "PREFIX", cPrefix, -1)
return strings.Replace(strings.Replace(cPrologGccgo, "PREFIX", cPrefix, -1),
"GCCGOSYMBOLPREF", p.gccgoSymbolPrefix(), -1)
}
const cPrologGccgo = `
@ -1348,6 +1403,39 @@ void *_cgoPREFIX_Cfunc__CMalloc(size_t n) {
runtime_throw("runtime: C malloc failed");
return p;
}
struct __go_type_descriptor;
typedef struct __go_empty_interface {
const struct __go_type_descriptor *__type_descriptor;
void *__object;
} Eface;
extern Eface runtimeCgoCheckPointer(Eface, Slice)
__asm__("runtime.cgoCheckPointer")
__attribute__((weak));
extern Eface localCgoCheckPointer(Eface, Slice)
__asm__("GCCGOSYMBOLPREF._cgoCheckPointer");
Eface localCgoCheckPointer(Eface ptr, Slice args) {
if(runtimeCgoCheckPointer) {
return runtimeCgoCheckPointer(ptr, args);
}
return ptr;
}
extern void runtimeCgoCheckResult(Eface)
__asm__("runtime.cgoCheckResult")
__attribute__((weak));
extern void localCgoCheckResult(Eface)
__asm__("GCCGOSYMBOLPREF._cgoCheckResult");
void localCgoCheckResult(Eface val) {
if(runtimeCgoCheckResult) {
runtimeCgoCheckResult(val);
}
}
`
func (p *Package) gccExportHeaderProlog() string {
@ -1373,14 +1461,16 @@ typedef GoUintGOINTBITS GoUint;
typedef __SIZE_TYPE__ GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
typedef __complex float GoComplex64;
typedef __complex double GoComplex128;
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
// static assertion to make sure the file is being used on architecture
// at least with matching size of GoInt.
/*
static assertion to make sure the file is being used on architecture
at least with matching size of GoInt.
*/
typedef char _check_for_GOINTBITS_bit_pointer_matching_GoInt[sizeof(void*)==GOINTBITS/8 ? 1:-1];
typedef struct { char *p; GoInt n; } GoString;
typedef struct { const char *p; GoInt n; } GoString;
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;

View File

@ -84,12 +84,16 @@ and test commands:
-n
print the commands but do not run them.
-p n
the number of builds that can be run in parallel.
the number of programs, such as build commands or
test binaries, that can be run in parallel.
The default is the number of CPUs available, except
on darwin/arm which defaults to 1.
-race
enable data race detection.
Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64.
-msan
enable interoperation with memory sanitizer.
Supported only on linux/amd64.
-v
print the names of packages as they are compiled.
-work
@ -112,13 +116,14 @@ and test commands:
a suffix to use in the name of the package installation directory,
in order to keep output separate from default builds.
If using the -race flag, the install suffix is automatically set to race
or, if set explicitly, has _race appended to it. Using a -buildmode
option that requires non-default compile flags has a similar effect.
or, if set explicitly, has _race appended to it. Likewise for the -msan
flag. Using a -buildmode option that requires non-default compile flags
has a similar effect.
-ldflags 'flag list'
arguments to pass on each go tool link invocation.
-linkshared
link against shared libraries previously created with
-buildmode=shared
-buildmode=shared.
-pkgdir dir
install and load all packages from dir instead of the usual locations.
For example, when building with a non-standard configuration,
@ -225,12 +230,17 @@ which is schematically one of these:
go doc <pkg>
go doc <sym>[.<method>]
go doc [<pkg>].<sym>[.<method>]
go doc [<pkg>.]<sym>[.<method>]
go doc [<pkg>.][<sym>.]<method>
The first item in this list matched by the argument is the one whose
documentation is printed. (See the examples below.) For packages, the order of
scanning is determined lexically, but the GOROOT tree is always scanned before
GOPATH.
The first item in this list matched by the argument is the one whose documentation
is printed. (See the examples below.) However, if the argument starts with a capital
letter it is assumed to identify a symbol or method in the current directory.
For packages, the order of scanning is determined lexically in breadth-first order.
That is, the package presented is the one that matches the search and is nearest
the root and lexically first at its level of the hierarchy. The GOROOT tree is
always scanned in its entirety before GOPATH.
If there is no package specified or matched, the package in the current
directory is selected, so "go doc Foo" shows the documentation for symbol Foo in
@ -278,6 +288,14 @@ Examples:
go doc text/template new # Two arguments
Show documentation for text/template's New function.
At least in the current tree, these invocations all print the
documentation for json.Decoder's Decode method:
go doc json.Decoder.Decode
go doc json.decoder.decode
go doc json.decode
cd go/src/encoding/json; go doc decode
Flags:
-c
Respect case when matching symbols.
@ -344,7 +362,7 @@ Generate Go files by processing source
Usage:
go generate [-run regexp] [file.go... | packages]
go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]
Generate runs commands described by directives within existing
files. Those commands can run any process but the intent is to
@ -436,12 +454,14 @@ Go generate accepts one specific flag:
any trailing spaces and final newline) matches the
expression.
It also accepts the standard build flags -v, -n, and -x.
It also accepts the standard build flags including -v, -n, and -x.
The -v flag prints the names of packages and files as they are
processed.
The -n flag prints commands that would be executed.
The -x flag prints commands as they are executed.
For more about build flags, see 'go help build'.
For more about specifying packages, see 'go help packages'.
@ -477,16 +497,22 @@ missing packages but does not use it to look for updates to existing packages.
Get also accepts build flags to control the installation. See 'go help build'.
When checking out a new package, get creates the target directory
GOPATH/src/<import-path>. If the GOPATH contains multiple entries,
get uses the first one. See 'go help gopath'.
When checking out or updating a package, get looks for a branch or tag
that matches the locally installed version of Go. The most important
rule is that if the local installation is running version "go1", get
searches for a branch or tag named "go1". If no such version exists it
retrieves the most recent version of the package.
If the vendoring experiment is enabled (see 'go help gopath'),
then when go get checks out or updates a Git repository,
Unless vendoring support is disabled (see 'go help gopath'),
when go get checks out or updates a Git repository,
it also updates any git submodules referenced by the repository.
Get never checks out or updates code stored in vendor directories.
For more about specifying packages, see 'go help packages'.
For more about how 'go get' finds source code to
@ -577,6 +603,14 @@ syntax of package template. The default output is equivalent to -f
XTestImports []string // imports from XTestGoFiles
}
The error information, if any, is
type PackageError struct {
ImportStack []string // shortest path from package named on command line to this one
Pos string // position of error (if present, file:line:col)
Err string // the error itself
}
The template function "join" calls strings.Join.
The template function "context" returns the build context, defined as:
@ -643,7 +677,7 @@ Test packages
Usage:
go test [-c] [-i] [build and test flags] [packages] [flags for test binary]
go test [build/test flags] [packages] [build/test flags & test binary flags]
'Go test' automates testing the packages named by the import paths.
It prints a summary of the test results in the format:
@ -673,10 +707,16 @@ non-test installation.
In addition to the build flags, the flags handled by 'go test' itself are:
-args
Pass the remainder of the command line (everything after -args)
to the test binary, uninterpreted and unchanged.
Because this flag consumes the remainder of the command line,
the package list (if present) must appear before this flag.
-c
Compile the test binary to pkg.test but do not run it
(where pkg is the last element of the package's import path).
The file name can be changed with the -o flag.
Compile the test binary to pkg.test but do not run it
(where pkg is the last element of the package's import path).
The file name can be changed with the -o flag.
-exec xprog
Run the test binary using xprog. The behavior is the same as
@ -687,17 +727,12 @@ In addition to the build flags, the flags handled by 'go test' itself are:
Do not run the test.
-o file
Compile the test binary to the named file.
The test still runs (unless -c or -i is specified).
Compile the test binary to the named file.
The test still runs (unless -c or -i is specified).
The test binary also accepts flags that control execution of the test; these
flags are also accessible by 'go test'. See 'go help testflag' for details.
If the test binary needs any other flags, they should be presented after the
package names. The go tool treats as a flag the first argument that begins with
a minus sign that it does not recognize itself; that argument and all subsequent
arguments are passed as arguments to the test binary.
For more about build flags, see 'go help build'.
For more about specifying packages, see 'go help packages'.
@ -804,6 +839,11 @@ are:
Build the listed main packages and everything they import into
executables. Packages not named main are ignored.
-buildmode=pie
Build the listed main packages and everything they import into
position independent executables (PIE). Packages not named
main are ignored.
File types
@ -875,7 +915,7 @@ DIR/bin/quux, not DIR/bin/foo/quux. The "foo/" prefix is stripped
so that you can add DIR/bin to your PATH to get at the
installed commands. If the GOBIN environment variable is
set, commands are installed to the directory it names instead
of DIR/bin.
of DIR/bin. GOBIN must be an absolute path.
Here's an example directory layout:
@ -933,13 +973,10 @@ See https://golang.org/s/go14internal for details.
Vendor Directories
Go 1.5 includes experimental support for using local copies
of external dependencies to satisfy imports of those dependencies,
often referred to as vendoring. Setting the environment variable
GO15VENDOREXPERIMENT=1 enables that experimental support.
Go 1.6 includes support for using local copies of external dependencies
to satisfy imports of those dependencies, often referred to as vendoring.
When the vendor experiment is enabled,
code below a directory named "vendor" is importable only
Code below a directory named "vendor" is importable only
by code in the directory tree rooted at the parent of "vendor",
and only using an import path that omits the prefix up to and
including the vendor element.
@ -977,12 +1014,18 @@ top-level "crash/bang".
Code in vendor directories is not subject to import path
checking (see 'go help importpath').
When the vendor experiment is enabled, 'go get' checks out
submodules when checking out or updating a git repository
(see 'go help get').
When 'go get' checks out or updates a git repository, it now also
updates submodules.
The vendoring semantics are an experiment, and they may change
in future releases. Once settled, they will be on by default.
Vendor directories do not affect the placement of new repositories
being checked out for the first time by 'go get': those are always
placed in the main GOPATH, never in a vendor subtree.
In Go 1.5, as an experiment, setting the environment variable
GO15VENDOREXPERIMENT=1 enabled these features.
As of Go 1.6 they are on by default. To turn them off, set
GO15VENDOREXPERIMENT=0. In Go 1.7, the environment
variable will stop having any effect.
See https://golang.org/s/go15vendor for details.
@ -1051,7 +1094,7 @@ Special-purpose environment variables:
File names in stack traces are rewritten from GOROOT to
GOROOT_FINAL.
GO15VENDOREXPERIMENT
Set to 1 to enable the Go 1.5 vendoring experiment.
Set to 0 to disable vendoring semantics.
GO_EXTLINK_ENABLED
Whether the linker should use external linking mode
when using -linkmode=auto with code that uses cgo.
@ -1227,10 +1270,10 @@ unless it is being referred to by that import path. In this way, import comments
let package authors make sure the custom import path is used and not a
direct path to the underlying code hosting site.
If the vendoring experiment is enabled (see 'go help gopath'),
then 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.
If vendoring is enabled (see 'go help gopath'), then 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.
See https://golang.org/s/go14customimport for details.
@ -1286,6 +1329,14 @@ internally at Google all begin with 'google', and paths
denoting remote repositories begin with the path to the code,
such as 'github.com/user/repo'.
Packages in a program need not have unique package names,
but there are two reserved package names with special meaning.
The name main indicates a command, not a library.
Commands are built into binaries and cannot be imported.
The name documentation indicates documentation for
a non-Go program in the directory. Files in package documentation
are ignored by the go command.
As a special case, if the package list is a list of .go files from a
single directory, the command is applied to a single synthesized
package made up of exactly those files, ignoring any build constraints
@ -1391,6 +1442,10 @@ control the execution of any test:
Allow parallel execution of test functions that call t.Parallel.
The value of this flag is the maximum number of tests to run
simultaneously; by default, it is set to the value of GOMAXPROCS.
Note that -parallel only applies within a single test binary.
The 'go test' command may run tests for different packages
in parallel as well, according to the setting of the -p flag
(see 'go help build').
-run regexp
Run only those tests and examples matching the regular
@ -1414,25 +1469,63 @@ control the execution of any test:
Verbose output: log all tests as they are run. Also print all
text from Log and Logf calls even if the test succeeds.
The test binary, called pkg.test where pkg is the name of the
directory containing the package sources, can be invoked directly
after building it with 'go test -c'. When invoking the test binary
directly, each of the standard flag names must be prefixed with 'test.',
as in -test.run=TestMyFunc or -test.v.
Each of these flags is also recognized with an optional 'test.' prefix,
as in -test.v. When invoking the generated test binary (the result of
'go test -c') directly, however, the prefix is mandatory.
When running 'go test', flags not listed above are passed through
unaltered. For instance, the command
The 'go test' command rewrites or removes recognized flags,
as appropriate, both before and after the optional package list,
before invoking the test binary.
go test -x -v -cpuprofile=prof.out -dir=testdata -update
For instance, the command
go test -v -myflag testdata -cpuprofile=prof.out -x
will compile the test binary and then run it as
pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update
pkg.test -test.v -myflag testdata -test.cpuprofile=prof.out
(The -x flag is removed because it applies only to the go command's
execution, not to the test itself.)
The test flags that generate profiles (other than for coverage) also
leave the test binary in pkg.test for use when analyzing the profiles.
Flags not recognized by 'go test' must be placed after any specified packages.
When 'go test' runs a test binary, it does so from within the
corresponding package's source code directory. Depending on the test,
it may be necessary to do the same when invoking a generated test
binary directly.
The command-line package list, if present, must appear before any
flag not known to the go test command. Continuing the example above,
the package list would have to appear before -myflag, but could appear
on either side of -v.
To keep an argument for a test binary from being interpreted as a
known flag or a package name, use -args (see 'go help test') which
passes the remainder of the command line through to the test binary
uninterpreted and unaltered.
For instance, the command
go test -v -args -x -v
will compile the test binary and then run it as
pkg.test -test.v -x -v
Similarly,
go test -args math
will compile the test binary and then run it as
pkg.test math
In the first example, the -x and the second -v are passed through to the
test binary unchanged and with no effect on the go command itself.
In the second example, the argument math is passed through to the test
binary, instead of being interpreted as the package list.
Description of testing functions

View File

@ -63,12 +63,16 @@ and test commands:
-n
print the commands but do not run them.
-p n
the number of builds that can be run in parallel.
the number of programs, such as build commands or
test binaries, that can be run in parallel.
The default is the number of CPUs available, except
on darwin/arm which defaults to 1.
-race
enable data race detection.
Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64.
-msan
enable interoperation with memory sanitizer.
Supported only on linux/amd64.
-v
print the names of packages as they are compiled.
-work
@ -91,13 +95,14 @@ and test commands:
a suffix to use in the name of the package installation directory,
in order to keep output separate from default builds.
If using the -race flag, the install suffix is automatically set to race
or, if set explicitly, has _race appended to it. Using a -buildmode
option that requires non-default compile flags has a similar effect.
or, if set explicitly, has _race appended to it. Likewise for the -msan
flag. Using a -buildmode option that requires non-default compile flags
has a similar effect.
-ldflags 'flag list'
arguments to pass on each go tool link invocation.
-linkshared
link against shared libraries previously created with
-buildmode=shared
-buildmode=shared.
-pkgdir dir
install and load all packages from dir instead of the usual locations.
For example, when building with a non-standard configuration,
@ -166,6 +171,7 @@ var buildGcflags []string // -gcflags flag
var buildLdflags []string // -ldflags flag
var buildGccgoflags []string // -gccgoflags flag
var buildRace bool // -race flag
var buildMSan bool // -msan flag
var buildToolExec []string // -toolexec flag
var buildBuildmode string // -buildmode flag
var buildLinkshared bool // -linkshared flag
@ -227,6 +233,7 @@ func addBuildFlags(cmd *Command) {
cmd.Flag.BoolVar(&buildLinkshared, "linkshared", false, "")
cmd.Flag.StringVar(&buildPkgdir, "pkgdir", "", "")
cmd.Flag.BoolVar(&buildRace, "race", false, "")
cmd.Flag.BoolVar(&buildMSan, "msan", false, "")
cmd.Flag.Var((*stringsFlag)(&buildContext.BuildTags), "tags", "")
cmd.Flag.Var((*stringsFlag)(&buildToolExec), "toolexec", "")
cmd.Flag.BoolVar(&buildWork, "work", false, "")
@ -352,29 +359,46 @@ func buildModeInit() {
codegenArg = "-fPIC"
} else {
switch platform {
case "linux/amd64":
case "linux/amd64", "linux/arm", "linux/arm64", "linux/386",
"android/amd64", "android/arm", "android/arm64", "android/386":
codegenArg = "-shared"
case "linux/arm":
buildAsmflags = append(buildAsmflags, "-shared")
case "darwin/amd64":
case "android/arm":
case "darwin/amd64", "darwin/386":
default:
fatalf("-buildmode=c-shared not supported on %s\n", platform)
}
}
ldBuildmode = "c-shared"
case "default":
ldBuildmode = "exe"
switch platform {
case "android/arm", "android/arm64", "android/amd64", "android/386":
codegenArg = "-shared"
ldBuildmode = "pie"
default:
ldBuildmode = "exe"
}
case "exe":
pkgsFilter = pkgsMain
ldBuildmode = "exe"
case "pie":
if gccgo {
fatalf("-buildmode=pie not supported by gccgo")
} else {
switch platform {
case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le",
"android/amd64", "android/arm", "android/arm64", "android/386":
codegenArg = "-shared"
default:
fatalf("-buildmode=pie not supported on %s\n", platform)
}
}
ldBuildmode = "pie"
case "shared":
pkgsFilter = pkgsNotMain
if gccgo {
codegenArg = "-fPIC"
} else {
switch platform {
case "linux/amd64":
case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le":
default:
fatalf("-buildmode=shared not supported on %s\n", platform)
}
@ -391,9 +415,11 @@ func buildModeInit() {
if gccgo {
codegenArg = "-fPIC"
} else {
if platform != "linux/amd64" {
fmt.Fprintf(os.Stderr, "go %s: -linkshared is only supported on linux/amd64\n", flag.Args()[0])
os.Exit(2)
switch platform {
case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le":
buildAsmflags = append(buildAsmflags, "-D=GOBUILDMODE_shared=1")
default:
fatalf("-linkshared not supported on %s\n", platform)
}
codegenArg = "-dynlink"
// TODO(mwhudson): remove -w when that gets fixed in linker.
@ -415,7 +441,7 @@ func buildModeInit() {
}
func runBuild(cmd *Command, args []string) {
raceInit()
instrumentInit()
buildModeInit()
var b builder
b.init()
@ -463,7 +489,12 @@ func runBuild(cmd *Command, args []string) {
var a *action
if buildBuildmode == "shared" {
a = b.libaction(libname(args), pkgsFilter(packages(args)), modeBuild, depMode)
pkgs := pkgsFilter(packages(args))
if libName, err := libname(args, pkgs); err != nil {
fatalf("%s", err.Error())
} else {
a = b.libaction(libName, pkgs, modeBuild, depMode)
}
} else {
a = &action{}
for _, p := range pkgsFilter(packages(args)) {
@ -487,32 +518,73 @@ See also: go build, go get, go clean.
`,
}
// 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"
}
// libname returns the filename to use for the shared library when using
// -buildmode=shared. The rules we use are:
// 1) Drop any trailing "/..."s if present
// 2) Change / to -
// 3) Join arguments with ,
// So std -> libstd.so
// a b/... -> liba,b.so
// gopkg.in/tomb.v2 -> libgopkg.in-tomb.v2.so
func libname(args []string) string {
// Use arguments for special 'meta' packages:
// std --> libstd.so
// std cmd --> libstd,cmd.so
// A single non-meta argument with trailing "/..." is special cased:
// foo/... --> libfoo.so
// (A relative path like "./..." expands the "." first)
// Use import paths for other cases, changing '/' to '-':
// somelib --> libsubdir-somelib.so
// ./ or ../ --> libsubdir-somelib.so
// gopkg.in/tomb.v2 -> libgopkg.in-tomb.v2.so
// a/... b/... ---> liba/c,b/d.so - all matching import paths
// Name parts are joined with ','.
func libname(args []string, pkgs []*Package) (string, error) {
var libname string
for _, arg := range args {
arg = strings.TrimSuffix(arg, "/...")
arg = strings.Replace(arg, "/", "-", -1)
appendName := func(arg string) {
if libname == "" {
libname = arg
} else {
libname += "," + arg
}
}
var haveNonMeta bool
for _, arg := range args {
if isMetaPackage(arg) {
appendName(arg)
} else {
haveNonMeta = true
}
}
if len(libname) == 0 { // non-meta packages only. use import paths
if len(args) == 1 && strings.HasSuffix(args[0], "/...") {
// Special case of "foo/..." as mentioned above.
arg := strings.TrimSuffix(args[0], "/...")
if build.IsLocalImport(arg) {
cwd, _ := os.Getwd()
bp, _ := buildContext.ImportDir(filepath.Join(cwd, arg), build.FindOnly)
if bp.ImportPath != "" && bp.ImportPath != "." {
arg = bp.ImportPath
}
}
appendName(strings.Replace(arg, "/", "-", -1))
} else {
for _, pkg := range pkgs {
appendName(strings.Replace(pkg.ImportPath, "/", "-", -1))
}
}
} else if haveNonMeta { // have both meta package and a non-meta one
return "", errors.New("mixing of meta and non-meta packages is not allowed")
}
// TODO(mwhudson): Needs to change for platforms that use different naming
// conventions...
return "lib" + libname + ".so"
return "lib" + libname + ".so", nil
}
func runInstall(cmd *Command, args []string) {
raceInit()
if gobin != "" && !filepath.IsAbs(gobin) {
fatalf("cannot install, GOBIN must be an absolute path")
}
instrumentInit()
buildModeInit()
pkgs := pkgsFilter(packagesForBuild(args))
@ -537,7 +609,11 @@ func runInstall(cmd *Command, args []string) {
b.init()
var a *action
if buildBuildmode == "shared" {
a = b.libaction(libname(args), pkgs, modeInstall, modeInstall)
if libName, err := libname(args, pkgs); err != nil {
fatalf("%s", err.Error())
} else {
a = b.libaction(libName, pkgs, modeInstall, modeInstall)
}
} else {
a = &action{}
var tools []*action
@ -754,7 +830,9 @@ func goFilesPackage(gofiles []string) *Package {
pkg := new(Package)
pkg.local = true
pkg.cmdline = true
stk.push("main")
pkg.load(&stk, bp, err)
stk.pop()
pkg.localPrefix = dirToImportPath(dir)
pkg.ImportPath = "command-line-arguments"
pkg.target = ""
@ -812,15 +890,17 @@ func readpkglist(shlibpath string) (pkgs []*Package) {
// action returns the action for applying the given operation (mode) to the package.
// depMode is the action to use when building dependencies.
// action never looks for p in a shared library.
// action never looks for p in a shared library, but may find p's dependencies in a
// shared library if buildLinkshared is true.
func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action {
return b.action1(mode, depMode, p, false)
return b.action1(mode, depMode, p, false, "")
}
// action1 returns the action for applying the given operation (mode) to the package.
// depMode is the action to use when building dependencies.
// action1 will look for p in a shared library if lookshared is true.
func (b *builder) action1(mode buildMode, depMode buildMode, p *Package, lookshared bool) *action {
// forShlib is the shared library that p will become part of, if any.
func (b *builder) action1(mode buildMode, depMode buildMode, p *Package, lookshared bool, forShlib string) *action {
shlib := ""
if lookshared {
shlib = p.Shlib
@ -852,13 +932,23 @@ func (b *builder) action1(mode buildMode, depMode buildMode, p *Package, looksha
b.actionCache[key] = a
for _, p1 := range p.imports {
ls := buildLinkshared
// If p1 is part of the same shared library as p, we need the action
// that builds p here, not the shared libary or we get action loops.
if p1.Shlib == p.Shlib {
ls = false
if forShlib != "" {
// p is part of a shared library.
if p1.Shlib != "" && p1.Shlib != forShlib {
// p1 is explicitly part of a different shared library.
// Put the action for that shared library into a.deps.
a.deps = append(a.deps, b.action1(depMode, depMode, p1, true, p1.Shlib))
} else {
// p1 is (implicitly or not) part of this shared library.
// Put the action for p1 into a.deps.
a.deps = append(a.deps, b.action1(depMode, depMode, p1, false, forShlib))
}
} else {
// p is not part of a shared library.
// If p1 is in a shared library, put the action for that into
// a.deps, otherwise put the action for p1 into a.deps.
a.deps = append(a.deps, b.action1(depMode, depMode, p1, buildLinkshared, p1.Shlib))
}
a.deps = append(a.deps, b.action1(depMode, depMode, p1, ls))
}
// If we are not doing a cross-build, then record the binary we'll
@ -866,7 +956,7 @@ func (b *builder) action1(mode buildMode, depMode buildMode, p *Package, looksha
// using cgo, to make sure we do not overwrite the binary while
// a package is using it. If this is a cross-build, then the cgo we
// are writing is not the cgo we need to use.
if goos == runtime.GOOS && goarch == runtime.GOARCH && !buildRace && reqStdPkgSrc {
if goos == runtime.GOOS && goarch == runtime.GOARCH && !buildRace && !buildMSan && reqStdPkgSrc {
if (len(p.CgoFiles) > 0 || p.Standard && p.ImportPath == "runtime/cgo") && !buildLinkshared && buildBuildmode != "shared" {
var stk importStack
p1 := loadPackage("cmd/cgo", &stk)
@ -914,18 +1004,27 @@ func (b *builder) action1(mode buildMode, depMode buildMode, p *Package, looksha
switch mode {
case modeInstall:
a.f = (*builder).install
a.deps = []*action{b.action1(modeBuild, depMode, p, lookshared)}
a.deps = []*action{b.action1(modeBuild, depMode, p, lookshared, forShlib)}
a.target = a.p.target
// Install header for cgo in c-archive and c-shared modes.
if p.usesCgo() && (buildBuildmode == "c-archive" || buildBuildmode == "c-shared") {
hdrTarget := a.target[:len(a.target)-len(filepath.Ext(a.target))] + ".h"
if buildContext.Compiler == "gccgo" {
// For the header file, remove the "lib"
// added by go/build, so we generate pkg.h
// rather than libpkg.h.
dir, file := filepath.Split(hdrTarget)
file = strings.TrimPrefix(file, "lib")
hdrTarget = filepath.Join(dir, file)
}
ah := &action{
p: a.p,
deps: []*action{a.deps[0]},
f: (*builder).installHeader,
pkgdir: a.pkgdir,
objdir: a.objdir,
target: a.target[:len(a.target)-len(filepath.Ext(a.target))] + ".h",
target: hdrTarget,
}
a.deps = append(a.deps, ah)
}
@ -961,7 +1060,11 @@ func (b *builder) action1(mode buildMode, depMode buildMode, p *Package, looksha
func (b *builder) libaction(libname string, pkgs []*Package, mode, depMode buildMode) *action {
a := &action{}
if mode == modeBuild {
switch mode {
default:
fatalf("unrecognized mode %v", mode)
case modeBuild:
a.f = (*builder).linkShared
a.target = filepath.Join(b.work, libname)
for _, p := range pkgs {
@ -970,14 +1073,15 @@ func (b *builder) libaction(libname string, pkgs []*Package, mode, depMode build
}
a.deps = append(a.deps, b.action(depMode, depMode, p))
}
} else if mode == modeInstall {
case modeInstall:
// Currently build mode shared forces external linking mode, and
// external linking mode forces an import of runtime/cgo. So if it
// was not passed on the command line and it is not present in
// another shared library, add it here.
seencgo := false
// external linking mode forces an import of runtime/cgo (and
// math on arm). So if it was not passed on the command line and
// it is not present in another shared library, add it here.
_, gccgo := buildToolchain.(gccgoToolchain)
if !gccgo {
seencgo := false
for _, p := range pkgs {
seencgo = seencgo || (p.Standard && p.ImportPath == "runtime/cgo")
}
@ -997,6 +1101,28 @@ func (b *builder) libaction(libname string, pkgs []*Package, mode, depMode build
pkgs = append(pkgs, p)
}
}
if goarch == "arm" {
seenmath := false
for _, p := range pkgs {
seenmath = seenmath || (p.Standard && p.ImportPath == "math")
}
if !seenmath {
var stk importStack
p := loadPackage("math", &stk)
if p.Error != nil {
fatalf("load math: %v", p.Error)
}
computeStale(p)
// If math is in another shared library, then that's
// also the shared library that contains runtime, so
// something will depend on it and so math's staleness
// will be checked when processing that library.
if p.Shlib == "" || p.Shlib == libname {
pkgs = append([]*Package{}, pkgs...)
pkgs = append(pkgs, p)
}
}
}
}
// Figure out where the library will go.
@ -1029,7 +1155,7 @@ func (b *builder) libaction(libname string, pkgs []*Package, mode, depMode build
if err != nil || lstat.ModTime().After(built) {
stale = true
}
a.deps = append(a.deps, b.action(depMode, depMode, p))
a.deps = append(a.deps, b.action1(depMode, depMode, p, false, a.target))
}
if stale {
@ -1047,8 +1173,6 @@ func (b *builder) libaction(libname string, pkgs []*Package, mode, depMode build
shlibnameaction.deps = append(shlibnameaction.deps, buildAction)
}
}
} else {
fatalf("unregonized mode %v", mode)
}
return a
}
@ -1239,17 +1363,11 @@ func (b *builder) build(a *action) (err error) {
// different sections of the bootstrap script have to
// be merged, the banners give patch something
// to use to find its context.
fmt.Printf("\n#\n# %s\n#\n\n", a.p.ImportPath)
b.print("\n#\n# " + a.p.ImportPath + "\n#\n\n")
}
if buildV {
fmt.Fprintf(os.Stderr, "%s\n", a.p.ImportPath)
}
if a.p.Standard && a.p.ImportPath == "runtime" && buildContext.Compiler == "gc" &&
(!hasString(a.p.GoFiles, "zgoos_"+buildContext.GOOS+".go") ||
!hasString(a.p.GoFiles, "zgoarch_"+buildContext.GOARCH+".go")) {
return fmt.Errorf("%s/%s must be bootstrapped using make%v", buildContext.GOOS, buildContext.GOARCH, defaultSuffix())
b.print(a.p.ImportPath + "\n")
}
// Make build directory.
@ -1393,17 +1511,17 @@ func (b *builder) build(a *action) (err error) {
switch {
case strings.HasSuffix(name, _goos_goarch):
targ := file[:len(name)-len(_goos_goarch)] + "_GOOS_GOARCH." + ext
if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0644, true); err != nil {
if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0666, true); err != nil {
return err
}
case strings.HasSuffix(name, _goarch):
targ := file[:len(name)-len(_goarch)] + "_GOARCH." + ext
if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0644, true); err != nil {
if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0666, true); err != nil {
return err
}
case strings.HasSuffix(name, _goos):
targ := file[:len(name)-len(_goos)] + "_GOOS." + ext
if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0644, true); err != nil {
if err := b.copyFile(a, obj+targ, filepath.Join(a.p.Dir, file), 0666, true); err != nil {
return err
}
}
@ -1492,7 +1610,7 @@ func (b *builder) getPkgConfigFlags(p *Package) (cflags, ldflags []string, err e
func (b *builder) installShlibname(a *action) error {
a1 := a.deps[0]
err := ioutil.WriteFile(a.target, []byte(filepath.Base(a1.target)+"\n"), 0644)
err := ioutil.WriteFile(a.target, []byte(filepath.Base(a1.target)+"\n"), 0666)
if err != nil {
return err
}
@ -1516,12 +1634,12 @@ func (b *builder) install(a *action) (err error) {
}
}()
a1 := a.deps[0]
perm := os.FileMode(0644)
perm := os.FileMode(0666)
if a1.link {
switch buildBuildmode {
case "c-archive", "c-shared":
default:
perm = 0755
perm = 0777
}
}
@ -1595,7 +1713,25 @@ func (b *builder) moveOrCopyFile(a *action, dst, src string, perm os.FileMode, f
// If we can update the mode and rename to the dst, do it.
// Otherwise fall back to standard copy.
if err := os.Chmod(src, perm); err == nil {
// The perm argument is meant to be adjusted according to umask,
// but we don't know what the umask is.
// Create a dummy file to find out.
// This avoids build tags and works even on systems like Plan 9
// where the file mask computation incorporates other information.
mode := perm
f, err := os.OpenFile(filepath.Clean(dst)+"-go-tmp-umask", os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
if err == nil {
fi, err := f.Stat()
if err == nil {
mode = fi.Mode() & 0777
}
name := f.Name()
f.Close()
os.Remove(name)
}
if err := os.Chmod(src, mode); err == nil {
if err := os.Rename(src, dst); err == nil {
if buildX {
b.showcmd("", "mv %s %s", src, dst)
@ -1629,7 +1765,7 @@ func (b *builder) copyFile(a *action, dst, src string, perm os.FileMode, force b
if fi.IsDir() {
return fmt.Errorf("build output %q already exists and is a directory", dst)
}
if !force && !isObject(dst) {
if !force && fi.Mode().IsRegular() && !isObject(dst) {
return fmt.Errorf("build output %q already exists and is not an object file", dst)
}
}
@ -1641,7 +1777,7 @@ func (b *builder) copyFile(a *action, dst, src string, perm os.FileMode, force b
}
}
os.Remove(dst)
mayberemovefile(dst)
df, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
if err != nil && toolIsWindows {
// Windows does not allow deletion of a binary file
@ -1660,7 +1796,7 @@ func (b *builder) copyFile(a *action, dst, src string, perm os.FileMode, force b
_, err = io.Copy(df, sf)
df.Close()
if err != nil {
os.Remove(dst)
mayberemovefile(dst)
return fmt.Errorf("copying %s to %s: %v", src, dst, err)
}
return nil
@ -1682,7 +1818,7 @@ func (b *builder) installHeader(a *action) error {
}
}
return b.moveOrCopyFile(a, a.target, src, 0644, true)
return b.moveOrCopyFile(a, a.target, src, 0666, true)
}
// cover runs, in effect,
@ -1707,6 +1843,7 @@ var objectMagic = [][]byte{
{0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00}, // PE (Windows) as generated by 6l/8l and gcc
{0x00, 0x00, 0x01, 0xEB}, // Plan 9 i386
{0x00, 0x00, 0x8a, 0x97}, // Plan 9 amd64
{0x00, 0x00, 0x06, 0x47}, // Plan 9 arm
}
func isObject(s string) bool {
@ -1725,6 +1862,16 @@ func isObject(s string) bool {
return false
}
// mayberemovefile removes a file only if it is a regular file
// When running as a user with sufficient privileges, we may delete
// even device files, for example, which is not intended.
func mayberemovefile(s string) {
if fi, err := os.Lstat(s); err == nil && !fi.Mode().IsRegular() {
return
}
os.Remove(s)
}
// fmtcmd formats a command in the manner of fmt.Sprintf but also:
//
// If dir is non-empty and the script is not in dir right now,
@ -2103,7 +2250,7 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool,
if p.Name == "main" {
gcargs[1] = "main"
}
if p.Standard && p.ImportPath == "runtime" {
if p.Standard && (p.ImportPath == "runtime" || strings.HasPrefix(p.ImportPath, "runtime/internal")) {
// runtime compiles with a special gc flag to emit
// additional reflect type data.
gcargs = append(gcargs, "-+")
@ -2208,33 +2355,26 @@ func (gcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []s
for _, f := range ofiles {
absOfiles = append(absOfiles, mkAbs(objDir, f))
}
cmd := "c"
absAfile := mkAbs(objDir, afile)
appending := false
if _, err := os.Stat(absAfile); err == nil {
appending = true
cmd = "r"
// The archive file should have been created by the compiler.
// Since it used to not work that way, verify.
if _, err := os.Stat(absAfile); err != nil {
fatalf("os.Stat of archive file failed: %v", err)
}
cmdline := stringList("pack", cmd, absAfile, absOfiles)
if appending {
if buildN || buildX {
b.showcmd(p.Dir, "%s # internal", joinUnambiguously(cmdline))
}
if buildN {
return nil
}
if err := packInternal(b, absAfile, absOfiles); err != nil {
b.showOutput(p.Dir, p.ImportPath, err.Error()+"\n")
return errPrintedOutput
}
if buildN || buildX {
cmdline := stringList("pack", "r", absAfile, absOfiles)
b.showcmd(p.Dir, "%s # internal", joinUnambiguously(cmdline))
}
if buildN {
return nil
}
// Need actual pack.
cmdline[0] = tool("pack")
return b.run(p.Dir, p.ImportPath, nil, buildToolExec, cmdline)
if err := packInternal(b, absAfile, absOfiles); err != nil {
b.showOutput(p.Dir, p.ImportPath, err.Error()+"\n")
return errPrintedOutput
}
return nil
}
func packInternal(b *builder, afile string, ofiles []string) error {
@ -2445,11 +2585,11 @@ func (tools gccgoToolchain) asm(b *builder, p *Package, obj, ofile, sfile string
sfile = mkAbs(p.Dir, sfile)
defs := []string{"-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch}
if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" {
defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`)
defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath)
}
defs = tools.maybePIC(defs)
defs = append(defs, b.gccArchArgs()...)
return b.run(p.Dir, p.ImportPath, nil, tools.compiler(), "-c", "-I", obj, "-o", ofile, defs, sfile)
return b.run(p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", obj, "-c", "-o", ofile, defs, sfile)
}
func (gccgoToolchain) pkgpath(basedir string, p *Package) string {
@ -2464,7 +2604,7 @@ func (gccgoToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles
for _, f := range ofiles {
absOfiles = append(absOfiles, mkAbs(objDir, f))
}
return b.run(p.Dir, p.ImportPath, nil, "ar", "cru", mkAbs(objDir, afile), absOfiles)
return b.run(p.Dir, p.ImportPath, nil, "ar", "rc", mkAbs(objDir, afile), absOfiles)
}
func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions []*action, mainpkg string, ofiles []string) error {
@ -2599,6 +2739,10 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions
// libffi.
ldflags = append(ldflags, "-Wl,-r", "-nostdlib", "-Wl,--whole-archive", "-lgolibbegin", "-Wl,--no-whole-archive")
if b.gccSupportsNoPie() {
ldflags = append(ldflags, "-no-pie")
}
// We are creating an object file, so we don't want a build ID.
ldflags = b.disableBuildID(ldflags)
@ -2606,7 +2750,7 @@ func (tools gccgoToolchain) ld(b *builder, root *action, out string, allactions
out = out + ".o"
case "c-shared":
ldflags = append(ldflags, "-shared", "-nostdlib", "-Wl,--whole-archive", "-lgolibbegin", "-Wl,--no-whole-archive", "-lgo", "-lgcc_s", "-lgcc")
ldflags = append(ldflags, "-shared", "-nostdlib", "-Wl,--whole-archive", "-lgolibbegin", "-Wl,--no-whole-archive", "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc")
default:
fatalf("-buildmode=%s not supported for gccgo", ldBuildmode)
@ -2704,43 +2848,6 @@ func gccgoCleanPkgpath(p *Package) string {
return strings.Map(clean, gccgoPkgpath(p))
}
// libgcc returns the filename for libgcc, as determined by invoking gcc with
// the -print-libgcc-file-name option.
func (b *builder) libgcc(p *Package) (string, error) {
var buf bytes.Buffer
gccCmd := b.gccCmd(p.Dir)
prev := b.print
if buildN {
// In -n mode we temporarily swap out the builder's
// print function to capture the command-line. This
// let's us assign it to $LIBGCC and produce a valid
// buildscript for cgo packages.
b.print = func(a ...interface{}) (int, error) {
return fmt.Fprint(&buf, a...)
}
}
f, err := b.runOut(p.Dir, p.ImportPath, nil, gccCmd, "-print-libgcc-file-name")
if err != nil {
return "", fmt.Errorf("gcc -print-libgcc-file-name: %v (%s)", err, f)
}
if buildN {
s := fmt.Sprintf("LIBGCC=$(%s)\n", buf.Next(buf.Len()-1))
b.print = prev
b.print(s)
return "$LIBGCC", nil
}
// The compiler might not be able to find libgcc, and in that case,
// it will simply return "libgcc.a", which is of no use to us.
if !filepath.IsAbs(string(f)) {
return "", nil
}
return strings.Trim(string(f), "\r\n"), nil
}
// gcc runs the gcc C compiler to create an object from a single C file.
func (b *builder) gcc(p *Package, out string, flags []string, cfile string) error {
return b.ccompile(p, out, flags, cfile, b.gccCmd(p.Dir))
@ -2827,6 +2934,36 @@ func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string {
return a
}
// On systems with PIE (position independent executables) enabled by default,
// -no-pie must be passed when doing a partial link with -Wl,-r. But -no-pie is
// not supported by all compilers.
func (b *builder) gccSupportsNoPie() bool {
if goos != "linux" {
// On some BSD platforms, error messages from the
// compiler make it to the console despite cmd.Std*
// all being nil. As -no-pie is only required on linux
// systems so far, we only test there.
return false
}
src := filepath.Join(b.work, "trivial.c")
if err := ioutil.WriteFile(src, []byte{}, 0666); err != nil {
return false
}
cmdArgs := b.gccCmd(b.work)
cmdArgs = append(cmdArgs, "-no-pie", "-c", "trivial.c")
if buildN || buildX {
b.showcmd(b.work, "%s", joinUnambiguously(cmdArgs))
if buildN {
return false
}
}
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
cmd.Dir = b.work
cmd.Env = envForDir(cmd.Dir, os.Environ())
out, err := cmd.CombinedOutput()
return err == nil && !bytes.Contains(out, []byte("unrecognized"))
}
// gccArchArgs returns arguments to pass to gcc based on the architecture.
func (b *builder) gccArchArgs() []string {
switch goarch {
@ -2866,12 +3003,6 @@ func (b *builder) cflags(p *Package, def bool) (cppflags, cflags, cxxflags, ldfl
var cgoRe = regexp.MustCompile(`[/\\:]`)
var (
cgoLibGccFile string
cgoLibGccErr error
cgoLibGccFileOnce sync.Once
)
func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofiles, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) {
cgoCPPFLAGS, cgoCFLAGS, cgoCXXFLAGS, cgoLDFLAGS := b.cflags(p, true)
_, cgoexeCFLAGS, _, _ := b.cflags(p, false)
@ -2882,11 +3013,16 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
cgoLDFLAGS = append(cgoLDFLAGS, "-lobjc")
}
if buildMSan && p.ImportPath != "runtime/cgo" {
cgoCFLAGS = append([]string{"-fsanitize=memory"}, cgoCFLAGS...)
cgoLDFLAGS = append([]string{"-fsanitize=memory"}, cgoLDFLAGS...)
}
// Allows including _cgo_export.h from .[ch] files in the package.
cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", obj)
// cgo
// TODO: CGOPKGPATH, CGO_FLAGS?
// TODO: CGO_FLAGS?
gofiles := []string{obj + "_cgo_gotypes.go"}
cfiles := []string{"_cgo_main.c", "_cgo_export.c"}
for _, fn := range cgofiles {
@ -2902,7 +3038,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
if p.Standard && p.ImportPath == "runtime/cgo" {
cgoflags = append(cgoflags, "-import_runtime_cgo=false")
}
if p.Standard && (p.ImportPath == "runtime/race" || p.ImportPath == "runtime/cgo") {
if p.Standard && (p.ImportPath == "runtime/race" || p.ImportPath == "runtime/msan" || p.ImportPath == "runtime/cgo") {
cgoflags = append(cgoflags, "-import_syscall=false")
}
@ -2954,7 +3090,9 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
var linkobj []string
var bareLDFLAGS []string
// filter out -lsomelib, -l somelib, *.{so,dll,dylib}, and (on Darwin) -framework X
// When linking relocatable objects, various flags need to be
// filtered out as they are inapplicable and can cause some linkers
// to fail.
for i := 0; i < len(cgoLDFLAGS); i++ {
f := cgoLDFLAGS[i]
switch {
@ -2970,7 +3108,6 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
case strings.HasSuffix(f, ".dylib"),
strings.HasSuffix(f, ".so"),
strings.HasSuffix(f, ".dll"):
continue
// Remove any -fsanitize=foo flags.
// Otherwise the compiler driver thinks that we are doing final link
// and links sanitizer runtime into the object file. But we are not doing
@ -2979,27 +3116,27 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
// See issue 8788 for details.
case strings.HasPrefix(f, "-fsanitize="):
continue
// runpath flags not applicable unless building a shared
// object or executable; see issue 12115 for details. This
// is necessary as Go currently does not offer a way to
// specify the set of LDFLAGS that only apply to shared
// objects.
case strings.HasPrefix(f, "-Wl,-rpath"):
if f == "-Wl,-rpath" || f == "-Wl,-rpath-link" {
// Skip following argument to -rpath* too.
i++
}
default:
bareLDFLAGS = append(bareLDFLAGS, f)
}
}
cgoLibGccFileOnce.Do(func() {
cgoLibGccFile, cgoLibGccErr = b.libgcc(p)
})
if cgoLibGccFile == "" && cgoLibGccErr != nil {
return nil, nil, err
}
var staticLibs []string
if goos == "windows" {
// libmingw32 and libmingwex might also use libgcc, so libgcc must come last,
// and they also have some inter-dependencies, so must use linker groups.
// libmingw32 and libmingwex have some inter-dependencies,
// so must use linker groups.
staticLibs = []string{"-Wl,--start-group", "-lmingwex", "-lmingw32", "-Wl,--end-group"}
}
if cgoLibGccFile != "" {
staticLibs = append(staticLibs, cgoLibGccFile)
}
cflags := stringList(cgoCPPFLAGS, cgoCFLAGS)
for _, cfile := range cfiles {
@ -3045,7 +3182,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
linkobj = append(linkobj, p.SysoFiles...)
dynobj := obj + "_cgo_.o"
pie := goarch == "arm" && (goos == "linux" || goos == "android")
pie := (goarch == "arm" && goos == "linux") || goos == "android"
if pie { // we need to use -pie for Linux/ARM to get accurate imported sym
cgoLDFLAGS = append(cgoLDFLAGS, "-pie")
}
@ -3083,6 +3220,10 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
}
ldflags := stringList(bareLDFLAGS, "-Wl,-r", "-nostdlib", staticLibs)
if b.gccSupportsNoPie() {
ldflags = append(ldflags, "-no-pie")
}
// We are creating an object file, so we don't want a build ID.
ldflags = b.disableBuildID(ldflags)
@ -3217,7 +3358,7 @@ func (b *builder) swigIntSize(obj string) (intsize string, err error) {
return "$INTBITS", nil
}
src := filepath.Join(b.work, "swig_intsize.go")
if err = ioutil.WriteFile(src, []byte(swigIntSizeCode), 0644); err != nil {
if err = ioutil.WriteFile(src, []byte(swigIntSizeCode), 0666); err != nil {
return
}
srcs := []string{src}
@ -3339,32 +3480,38 @@ func (q *actionQueue) pop() *action {
return heap.Pop(q).(*action)
}
func raceInit() {
if !buildRace {
func instrumentInit() {
if !buildRace && !buildMSan {
return
}
if goarch != "amd64" || goos != "linux" && goos != "freebsd" && goos != "darwin" && goos != "windows" {
fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64\n", flag.Args()[0])
if buildRace && buildMSan {
fmt.Fprintf(os.Stderr, "go %s: may not use -race and -msan simultaneously", flag.Args()[0])
os.Exit(2)
}
buildGcflags = append(buildGcflags, "-race")
buildLdflags = append(buildLdflags, "-race")
if goarch != "amd64" || goos != "linux" && goos != "freebsd" && goos != "darwin" && goos != "windows" {
fmt.Fprintf(os.Stderr, "go %s: -race and -msan are only supported on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64\n", flag.Args()[0])
os.Exit(2)
}
if !buildContext.CgoEnabled {
fmt.Fprintf(os.Stderr, "go %s: -race requires cgo; enable cgo by setting CGO_ENABLED=1\n", flag.Args()[0])
os.Exit(2)
}
if buildRace {
buildGcflags = append(buildGcflags, "-race")
buildLdflags = append(buildLdflags, "-race")
} else {
buildGcflags = append(buildGcflags, "-msan")
buildLdflags = append(buildLdflags, "-msan")
}
if buildContext.InstallSuffix != "" {
buildContext.InstallSuffix += "_"
}
buildContext.InstallSuffix += "race"
buildContext.BuildTags = append(buildContext.BuildTags, "race")
}
// defaultSuffix returns file extension used for command files in
// current os environment.
func defaultSuffix() string {
switch runtime.GOOS {
case "windows":
return ".bat"
case "plan9":
return ".rc"
default:
return ".bash"
if buildRace {
buildContext.InstallSuffix += "race"
buildContext.BuildTags = append(buildContext.BuildTags, "race")
} else {
buildContext.InstallSuffix += "msan"
buildContext.BuildTags = append(buildContext.BuildTags, "msan")
}
}

View File

@ -41,9 +41,9 @@ func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) {
d.Strict = false
var t xml.Token
for {
t, err = d.Token()
t, err = d.RawToken()
if err != nil {
if err == io.EOF {
if err == io.EOF || len(imports) > 0 {
err = nil
}
return

View File

@ -32,12 +32,17 @@ which is schematically one of these:
go doc <pkg>
go doc <sym>[.<method>]
go doc [<pkg>].<sym>[.<method>]
go doc [<pkg>.]<sym>[.<method>]
go doc [<pkg>.][<sym>.]<method>
The first item in this list matched by the argument is the one whose
documentation is printed. (See the examples below.) For packages, the order of
scanning is determined lexically, but the GOROOT tree is always scanned before
GOPATH.
The first item in this list matched by the argument is the one whose documentation
is printed. (See the examples below.) However, if the argument starts with a capital
letter it is assumed to identify a symbol or method in the current directory.
For packages, the order of scanning is determined lexically in breadth-first order.
That is, the package presented is the one that matches the search and is nearest
the root and lexically first at its level of the hierarchy. The GOROOT tree is
always scanned in its entirety before GOPATH.
If there is no package specified or matched, the package in the current
directory is selected, so "go doc Foo" shows the documentation for symbol Foo in
@ -85,6 +90,14 @@ Examples:
go doc text/template new # Two arguments
Show documentation for text/template's New function.
At least in the current tree, these invocations all print the
documentation for json.Decoder's Decode method:
go doc json.Decoder.Decode
go doc json.decoder.decode
go doc json.decode
cd go/src/encoding/json; go doc decode
Flags:
-c
Respect case when matching symbols.

View File

@ -33,6 +33,11 @@ func mkEnv() []envVar {
var b builder
b.init()
vendorExpValue := "0"
if go15VendorExperiment {
vendorExpValue = "1"
}
env := []envVar{
{"GOARCH", goarch},
{"GOBIN", gobin},
@ -44,7 +49,7 @@ func mkEnv() []envVar {
{"GORACE", os.Getenv("GORACE")},
{"GOROOT", goroot},
{"GOTOOLDIR", toolDir},
{"GO15VENDOREXPERIMENT", os.Getenv("GO15VENDOREXPERIMENT")},
{"GO15VENDOREXPERIMENT", vendorExpValue},
// disable escape codes in clang errors
{"TERM", "dumb"},

View File

@ -22,7 +22,7 @@ import (
var cmdGenerate = &Command{
Run: runGenerate,
UsageLine: "generate [-run regexp] [file.go... | packages]",
UsageLine: "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
@ -115,12 +115,14 @@ Go generate accepts one specific flag:
any trailing spaces and final newline) matches the
expression.
It also accepts the standard build flags -v, -n, and -x.
It also accepts the standard build flags including -v, -n, and -x.
The -v flag prints the names of packages and files as they are
processed.
The -n flag prints commands that would be executed.
The -x flag prints commands as they are executed.
For more about build flags, see 'go help build'.
For more about specifying packages, see 'go help packages'.
`,
}
@ -179,6 +181,7 @@ type Generator struct {
pkg string
commands map[string][]string
lineNum int // current line number.
env []string
}
// run runs the generators in the current file.
@ -242,6 +245,7 @@ func (g *Generator) run() (ok bool) {
}
}
g.setEnv()
words := g.split(string(buf))
if len(words) == 0 {
g.errorf("no arguments to directive")
@ -269,6 +273,19 @@ func isGoGenerate(buf []byte) bool {
return bytes.HasPrefix(buf, []byte("//go:generate ")) || bytes.HasPrefix(buf, []byte("//go:generate\t"))
}
// setEnv sets the extra environment variables used when executing a
// single go:generate command.
func (g *Generator) setEnv() {
g.env = []string{
"GOARCH=" + runtime.GOARCH,
"GOOS=" + runtime.GOOS,
"GOFILE=" + g.file,
"GOLINE=" + strconv.Itoa(g.lineNum),
"GOPACKAGE=" + g.pkg,
"DOLLAR=" + "$",
}
}
// split breaks the line into words, evaluating quoted
// strings and evaluating environment variables.
// The initial //go:generate element is present in line.
@ -345,22 +362,13 @@ func (g *Generator) errorf(format string, args ...interface{}) {
// expandVar expands the $XXX invocation in word. It is called
// by os.Expand.
func (g *Generator) expandVar(word string) string {
switch word {
case "GOARCH":
return buildContext.GOARCH
case "GOOS":
return buildContext.GOOS
case "GOFILE":
return g.file
case "GOLINE":
return fmt.Sprint(g.lineNum)
case "GOPACKAGE":
return g.pkg
case "DOLLAR":
return "$"
default:
return os.Getenv(word)
w := word + "="
for _, e := range g.env {
if strings.HasPrefix(e, w) {
return e[len(w):]
}
}
return os.Getenv(word)
}
// identLength returns the length of the identifier beginning the string.
@ -396,13 +404,7 @@ func (g *Generator) exec(words []string) {
cmd.Stderr = os.Stderr
// Run the command in the package directory.
cmd.Dir = g.dir
env := []string{
"GOARCH=" + runtime.GOARCH,
"GOOS=" + runtime.GOOS,
"GOFILE=" + g.file,
"GOPACKAGE=" + g.pkg,
}
cmd.Env = mergeEnvLists(env, origEnv)
cmd.Env = mergeEnvLists(g.env, origEnv)
err := cmd.Run()
if err != nil {
g.errorf("running %q: %s", words[0], err)

View File

@ -39,6 +39,7 @@ func TestGenerateCommandParse(t *testing.T) {
pkg: "sys",
commands: make(map[string][]string),
}
g.setEnv()
g.setShorthand([]string{"-command", "yacc", "go", "tool", "yacc"})
for _, test := range splitTests {
// First with newlines.

View File

@ -45,16 +45,22 @@ missing packages but does not use it to look for updates to existing packages.
Get also accepts build flags to control the installation. See 'go help build'.
When checking out a new package, get creates the target directory
GOPATH/src/<import-path>. If the GOPATH contains multiple entries,
get uses the first one. See 'go help gopath'.
When checking out or updating a package, get looks for a branch or tag
that matches the locally installed version of Go. The most important
rule is that if the local installation is running version "go1", get
searches for a branch or tag named "go1". If no such version exists it
retrieves the most recent version of the package.
If the vendoring experiment is enabled (see 'go help gopath'),
then when go get checks out or updates a Git repository,
Unless vendoring support is disabled (see 'go help gopath'),
when go get checks out or updates a Git repository,
it also updates any git submodules referenced by the repository.
Get never checks out or updates code stored in vendor directories.
For more about specifying packages, see 'go help packages'.
For more about how 'go get' finds source code to
@ -84,8 +90,12 @@ func runGet(cmd *Command, args []string) {
// Disable any prompting for passwords by Git.
// Only has an effect for 2.3.0 or later, but avoiding
// the prompt in earlier versions is just too hard.
// See golang.org/issue/9341.
os.Setenv("GIT_TERMINAL_PROMPT", "0")
// If user has explicitly set GIT_TERMINAL_PROMPT=1, keep
// prompting.
// See golang.org/issue/9341 and golang.org/issue/12706.
if os.Getenv("GIT_TERMINAL_PROMPT") == "" {
os.Setenv("GIT_TERMINAL_PROMPT", "0")
}
// Phase 1. Download/update.
var stk importStack

View File

@ -31,8 +31,7 @@ var (
exeSuffix string // ".exe" on Windows
builder = testenv.Builder()
skipExternalBuilder = false // skip external tests on this builder
skipExternal = false // skip external tests
)
func init() {
@ -44,14 +43,21 @@ func init() {
case "arm", "arm64":
canRun = false
}
}
if strings.HasPrefix(builder+"-", "freebsd-arm-") {
skipExternalBuilder = true
canRun = false
}
switch runtime.GOOS {
case "linux":
switch runtime.GOARCH {
case "arm":
// many linux/arm machines are too slow to run
// the full set of external tests.
skipExternal = true
}
case "freebsd":
switch runtime.GOARCH {
case "arm":
// many freebsd/arm machines are too slow to run
// the full set of external tests.
skipExternal = true
canRun = false
}
case "windows":
exeSuffix = ".exe"
}
@ -83,8 +89,6 @@ func TestMain(m *testing.M) {
case "linux", "darwin", "freebsd", "windows":
canRace = canCgo && runtime.GOARCH == "amd64"
}
measureTick("./testgo" + exeSuffix)
}
// Don't let these environment variables confuse the test.
@ -103,24 +107,8 @@ func TestMain(m *testing.M) {
// The length of an mtime tick on this system. This is an estimate of
// how long we need to sleep to ensure that the mtime of two files is
// different.
var mtimeTick time.Duration
// measureTick sets mtimeTick by looking at the rounding of the mtime
// of a file.
func measureTick(path string) {
st, err := os.Stat(path)
if err != nil {
// Default to one second, the most conservative value.
mtimeTick = time.Second
return
}
mtime := st.ModTime()
t := time.Microsecond
for mtime.Round(t).Equal(mtime) && t < time.Second {
t *= 10
}
mtimeTick = t
}
// We used to try to be clever but that didn't always work (see golang.org/issue/12205).
var mtimeTick time.Duration = 1 * time.Second
// Manage a single run of the testgo binary.
type testgoData struct {
@ -138,8 +126,8 @@ type testgoData struct {
func testgo(t *testing.T) *testgoData {
testenv.MustHaveGoBuild(t)
if skipExternalBuilder {
t.Skip("skipping external tests on %s builder", builder)
if skipExternal {
t.Skip("skipping external tests on %s/%s", runtime.GOOS, runtime.GOARCH)
}
return &testgoData{t: t}
@ -452,7 +440,7 @@ func (tg *testgoData) grepCountBoth(match string) int {
// removed if it exists.
func (tg *testgoData) creatingTemp(path string) {
if filepath.IsAbs(path) && !strings.HasPrefix(path, tg.tempdir) {
tg.t.Fatal("internal testsuite error: creatingTemp(%q) with absolute path not in temporary directory", path)
tg.t.Fatalf("internal testsuite error: creatingTemp(%q) with absolute path not in temporary directory", path)
}
// If we have changed the working directory, make sure we have
// an absolute path, because we are going to change directory
@ -671,17 +659,48 @@ func TestGoBuildDashAInDevBranch(t *testing.T) {
tg.grepStderr("runtime", "testgo build -a math in dev branch DID NOT build runtime, but should have")
}
func TestGoBuilDashAInReleaseBranch(t *testing.T) {
func TestGoBuildDashAInReleaseBranch(t *testing.T) {
if testing.Short() {
t.Skip("don't rebuild the standard library in short mode")
}
tg := testgo(t)
defer tg.cleanup()
tg.run("install", "math") // should be up to date already but just in case
tg.run("install", "math", "net/http") // should be up to date already but just in case
tg.setenv("TESTGO_IS_GO_RELEASE", "1")
tg.run("build", "-v", "-a", "math")
tg.grepStderr("runtime", "testgo build -a math in dev branch did not build runtime, but should have")
tg.run("install", "-v", "-a", "math")
tg.grepStderr("runtime", "testgo build -a math in release branch DID NOT build runtime, but should have")
// Now runtime.a is updated (newer mtime), so everything would look stale if not for being a release.
//
tg.run("build", "-v", "net/http")
tg.grepStderrNot("strconv", "testgo build -v net/http in release branch with newer runtime.a DID build strconv but should not have")
tg.grepStderrNot("golang.org/x/net/http2/hpack", "testgo build -v net/http in release branch with newer runtime.a DID build .../golang.org/x/net/http2/hpack but should not have")
tg.grepStderrNot("net/http", "testgo build -v net/http in release branch with newer runtime.a DID build net/http but should not have")
}
func TestGoListStandard(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.cd(runtime.GOROOT() + "/src")
tg.run("list", "-f", "{{if not .Standard}}{{.ImportPath}}{{end}}", "./...")
stdout := tg.getStdout()
for _, line := range strings.Split(stdout, "\n") {
if strings.HasPrefix(line, "_/") && strings.HasSuffix(line, "/src") {
// $GOROOT/src shows up if there are any .go files there.
// We don't care.
continue
}
if line == "" {
continue
}
t.Errorf("package in GOROOT not listed as standard: %v", line)
}
// Similarly, expanding std should include some of our vendored code.
tg.run("list", "std", "cmd")
tg.grepStdout("golang.org/x/net/http2/hpack", "list std cmd did not mention vendored hpack")
tg.grepStdout("golang.org/x/arch/x86/x86asm", "list std cmd did not mention vendored x86asm")
}
func TestGoInstallCleansUpAfterGoBuild(t *testing.T) {
@ -775,6 +794,28 @@ func TestGoInstallDetectsRemovedFiles(t *testing.T) {
tg.wantStale("mypkg", "./testgo list mypkg claims mypkg is NOT stale after removing y.go; should be stale")
}
func TestWildcardMatchesSyntaxErrorDirs(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.tempFile("src/mypkg/x.go", `package mypkg`)
tg.tempFile("src/mypkg/y.go", `pkg mypackage`)
tg.setenv("GOPATH", tg.path("."))
tg.cd(tg.path("src/mypkg"))
tg.runFail("list", "./...")
tg.runFail("build", "./...")
tg.runFail("install", "./...")
}
func TestGoListWithTags(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.tempFile("src/mypkg/x.go", "// +build thetag\n\npackage mypkg\n")
tg.setenv("GOPATH", tg.path("."))
tg.cd(tg.path("./src"))
tg.run("list", "-tags=thetag", "./my...")
tg.grepStdout("mypkg", "did not find mypkg")
}
func TestGoInstallErrorOnCrossCompileToBin(t *testing.T) {
if testing.Short() {
t.Skip("don't install into GOROOT in short mode")
@ -951,6 +992,16 @@ func TestInternalPackagesOutsideGOROOTAreRespected(t *testing.T) {
tg.grepBoth("use of internal package not allowed", "wrote error message for testdata/testinternal2")
}
func TestRunInternal(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
dir := filepath.Join(tg.pwd(), "testdata")
tg.setenv("GOPATH", dir)
tg.run("run", filepath.Join(dir, "src/run/good.go"))
tg.runFail("run", filepath.Join(dir, "src/run/bad.go"))
tg.grepStderr("use of internal package not allowed", "unexpected error for run/bad.go")
}
func testMove(t *testing.T, vcs, url, base, config string) {
testenv.MustHaveExternalNetwork(t)
@ -1053,7 +1104,6 @@ func TestImportCommentConflict(t *testing.T) {
// cmd/go: custom import path checking should not apply to github.com/xxx/yyy.
func TestIssue10952(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
if _, err := exec.LookPath("git"); err != nil {
t.Skip("skipping because git binary not found")
}
@ -1071,6 +1121,34 @@ func TestIssue10952(t *testing.T) {
tg.run("get", "-d", "-u", importPath)
}
func TestGetGitDefaultBranch(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
if _, err := exec.LookPath("git"); err != nil {
t.Skip("skipping because git binary not found")
}
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.tempDir("src")
tg.setenv("GOPATH", tg.path("."))
// This repo has two branches, master and another-branch.
// The another-branch is the default that you get from 'git clone'.
// The go get command variants should not override this.
const importPath = "github.com/rsc/go-get-default-branch"
tg.run("get", "-d", importPath)
repoDir := tg.path("src/" + importPath)
defer tg.resetReadOnlyFlagAll(repoDir)
tg.runGit(repoDir, "branch", "--contains", "HEAD")
tg.grepStdout(`\* another-branch`, "not on correct default branch")
tg.run("get", "-d", "-u", importPath)
tg.runGit(repoDir, "branch", "--contains", "HEAD")
tg.grepStdout(`\* another-branch`, "not on correct default branch")
}
func TestDisallowedCSourceFiles(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
@ -1139,6 +1217,15 @@ func TestInstallFailsWithNoBuildableFiles(t *testing.T) {
tg.grepStderr("no buildable Go source files", "go install cgotest did not report 'no buildable Go Source files'")
}
func TestRelativeGOBINFail(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.tempFile("triv.go", `package main; func main() {}`)
tg.setenv("GOBIN", ".")
tg.runFail("install")
tg.grepStderr("cannot install, GOBIN must be an absolute path", "go install must fail if $GOBIN is a relative path")
}
// Test that without $GOBIN set, binaries get installed
// into the GOPATH bin directory.
func TestInstallIntoGOPATH(t *testing.T) {
@ -1150,9 +1237,21 @@ func TestInstallIntoGOPATH(t *testing.T) {
tg.wantExecutable("testdata/bin/go-cmd-test"+exeSuffix, "go install go-cmd-test did not write to testdata/bin/go-cmd-test")
}
// Issue 12407
func TestBuildOutputToDevNull(t *testing.T) {
if runtime.GOOS == "plan9" {
t.Skip("skipping because /dev/null is a regular file on plan9")
}
tg := testgo(t)
defer tg.cleanup()
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.run("build", "-o", os.DevNull, "go-cmd-test")
}
func TestPackageMainTestImportsArchiveNotBinary(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
gobin := filepath.Join(tg.pwd(), "testdata", "bin")
tg.creatingTemp(gobin)
tg.setenv("GOBIN", gobin)
@ -1165,6 +1264,17 @@ func TestPackageMainTestImportsArchiveNotBinary(t *testing.T) {
tg.run("test", "main_test")
}
// Issue 12690
func TestPackageNotStaleWithTrailingSlash(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
goroot := runtime.GOROOT()
tg.setenv("GOROOT", goroot+"/")
tg.wantNotStale("runtime", "with trailing slash in GOROOT, runtime listed as stale")
tg.wantNotStale("os", "with trailing slash in GOROOT, os listed as stale")
tg.wantNotStale("io", "with trailing slash in GOROOT, io listed as stale")
}
// With $GOBIN set, binaries get installed to $GOBIN.
func TestInstallIntoGOBIN(t *testing.T) {
tg := testgo(t)
@ -1357,6 +1467,18 @@ func TestGoListCmdOnlyShowsCommands(t *testing.T) {
}
}
func TestGoListDedupsPackages(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.run("list", "xtestonly", "./testdata/src/xtestonly/...")
got := strings.TrimSpace(tg.getStdout())
const want = "xtestonly"
if got != want {
t.Errorf("got %q; want %q", got, want)
}
}
// Issue 4096. Validate the output of unsuccessful go install foo/quxx.
func TestUnsuccessfulGoInstallShouldMentionMissingPackage(t *testing.T) {
tg := testgo(t)
@ -1833,6 +1955,9 @@ func TestIssue6480(t *testing.T) {
// cmd/cgo: undefined reference when linking a C-library using gccgo
func TestIssue7573(t *testing.T) {
if !canCgo {
t.Skip("skipping because cgo not enabled")
}
if _, err := exec.LookPath("gccgo"); err != nil {
t.Skip("skipping because no gccgo compiler found")
}
@ -1931,6 +2056,13 @@ func TestGoTestFooTestWorks(t *testing.T) {
tg.run("test", "testdata/standalone_test.go")
}
func TestGoTestFlagsAfterPackage(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.run("test", "testdata/flag_test.go", "-v", "-args", "-v=7") // Two distinct -v flags.
tg.run("test", "-v", "testdata/flag_test.go", "-args", "-v=7") // Two distinct -v flags.
}
func TestGoTestXtestonlyWorks(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
@ -1991,6 +2123,21 @@ func TestGoGenerateRunFlag(t *testing.T) {
tg.grepStdoutNot("no", "go generate -run yes ./testdata/generate/test4.go selected no")
}
func TestGoGenerateEnv(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
t.Skipf("skipping because %s does not have the env command", runtime.GOOS)
}
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.tempFile("env.go", "package main\n\n//go:generate env")
tg.run("generate", tg.path("env.go"))
for _, v := range []string{"GOARCH", "GOOS", "GOFILE", "GOLINE", "GOPACKAGE", "DOLLAR"} {
tg.grepStdout("^"+v+"=", "go generate environment missing "+v)
}
}
func TestGoGetCustomDomainWildcard(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
@ -2051,6 +2198,17 @@ func TestGoGetRscIoToolstash(t *testing.T) {
tg.run("get", "./toolstash")
}
// Issue 13037: Was not parsing <meta> tags in 404 served over HTTPS
func TestGoGetHTTPS404(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
tg := testgo(t)
defer tg.cleanup()
tg.tempDir("src")
tg.setenv("GOPATH", tg.path("."))
tg.run("get", "bazil.org/fuse/fs/fstestutil")
}
// Test that you can not import a main package.
func TestIssue4210(t *testing.T) {
tg := testgo(t)
@ -2128,7 +2286,11 @@ func TestGoGetInsecureCustomDomain(t *testing.T) {
}
func TestIssue10193(t *testing.T) {
t.Skip("depends on code.google.com")
testenv.MustHaveExternalNetwork(t)
if _, err := exec.LookPath("hg"); err != nil {
t.Skip("skipping because hg binary not found")
}
tg := testgo(t)
defer tg.cleanup()
@ -2199,7 +2361,7 @@ func TestGoTestImportErrorStack(t *testing.T) {
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.runFail("test", "testdep/p1")
if !strings.Contains(tg.stderr.String(), out) {
t.Fatal("did not give full import stack:\n\n%s", tg.stderr.String())
t.Fatalf("did not give full import stack:\n\n%s", tg.stderr.String())
}
}
@ -2387,3 +2549,12 @@ func TestGoBuildARM(t *testing.T) {
tg.run("build", "hello.go")
tg.grepStderrNot("unable to find math.a", "did not build math.a correctly")
}
func TestIssue13655(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
for _, pkg := range []string{"runtime", "runtime/internal/atomic"} {
tg.run("list", "-f", "{{.Deps}}", pkg)
tg.grepStdout("runtime/internal/sys", "did not find required dependency of "+pkg+" on runtime/internal/sys")
}
}

View File

@ -0,0 +1,31 @@
// 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 darwin dragonfly freebsd linux netbsd openbsd solaris
package main_test
import (
"os"
"syscall"
"testing"
)
func TestGoBuildUmask(t *testing.T) {
// Do not use tg.parallel; avoid other tests seeing umask manipulation.
mask := syscall.Umask(0077) // prohibit low bits
defer syscall.Umask(mask)
tg := testgo(t)
defer tg.cleanup()
tg.tempFile("x.go", `package main; func main() {}`)
tg.creatingTemp("x")
tg.run("build", tg.path("x.go"))
fi, err := os.Stat("x")
if err != nil {
t.Fatal(err)
}
if mode := fi.Mode(); mode&0077 != 0 {
t.Fatalf("wrote x with mode=%v, wanted no 0077 bits", mode)
}
}

View File

@ -79,6 +79,14 @@ internally at Google all begin with 'google', and paths
denoting remote repositories begin with the path to the code,
such as 'github.com/user/repo'.
Packages in a program need not have unique package names,
but there are two reserved package names with special meaning.
The name main indicates a command, not a library.
Commands are built into binaries and cannot be imported.
The name documentation indicates documentation for
a non-Go program in the directory. Files in package documentation
are ignored by the go command.
As a special case, if the package list is a list of .go files from a
single directory, the command is applied to a single synthesized
package made up of exactly those files, ignoring any build constraints
@ -261,10 +269,10 @@ unless it is being referred to by that import path. In this way, import comments
let package authors make sure the custom import path is used and not a
direct path to the underlying code hosting site.
If the vendoring experiment is enabled (see 'go help gopath'),
then 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.
If vendoring is enabled (see 'go help gopath'), then 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.
See https://golang.org/s/go14customimport for details.
`,
@ -307,7 +315,7 @@ DIR/bin/quux, not DIR/bin/foo/quux. The "foo/" prefix is stripped
so that you can add DIR/bin to your PATH to get at the
installed commands. If the GOBIN environment variable is
set, commands are installed to the directory it names instead
of DIR/bin.
of DIR/bin. GOBIN must be an absolute path.
Here's an example directory layout:
@ -365,13 +373,10 @@ See https://golang.org/s/go14internal for details.
Vendor Directories
Go 1.5 includes experimental support for using local copies
of external dependencies to satisfy imports of those dependencies,
often referred to as vendoring. Setting the environment variable
GO15VENDOREXPERIMENT=1 enables that experimental support.
Go 1.6 includes support for using local copies of external dependencies
to satisfy imports of those dependencies, often referred to as vendoring.
When the vendor experiment is enabled,
code below a directory named "vendor" is importable only
Code below a directory named "vendor" is importable only
by code in the directory tree rooted at the parent of "vendor",
and only using an import path that omits the prefix up to and
including the vendor element.
@ -409,12 +414,18 @@ top-level "crash/bang".
Code in vendor directories is not subject to import path
checking (see 'go help importpath').
When the vendor experiment is enabled, 'go get' checks out
submodules when checking out or updating a git repository
(see 'go help get').
When 'go get' checks out or updates a git repository, it now also
updates submodules.
The vendoring semantics are an experiment, and they may change
in future releases. Once settled, they will be on by default.
Vendor directories do not affect the placement of new repositories
being checked out for the first time by 'go get': those are always
placed in the main GOPATH, never in a vendor subtree.
In Go 1.5, as an experiment, setting the environment variable
GO15VENDOREXPERIMENT=1 enabled these features.
As of Go 1.6 they are on by default. To turn them off, set
GO15VENDOREXPERIMENT=0. In Go 1.7, the environment
variable will stop having any effect.
See https://golang.org/s/go15vendor for details.
`,
@ -487,7 +498,7 @@ Special-purpose environment variables:
File names in stack traces are rewritten from GOROOT to
GOROOT_FINAL.
GO15VENDOREXPERIMENT
Set to 1 to enable the Go 1.5 vendoring experiment.
Set to 0 to disable vendoring semantics.
GO_EXTLINK_ENABLED
Whether the linker should use external linking mode
when using -linkmode=auto with code that uses cgo.
@ -570,5 +581,10 @@ are:
-buildmode=exe
Build the listed main packages and everything they import into
executables. Packages not named main are ignored.
-buildmode=pie
Build the listed main packages and everything they import into
position independent executables (PIE). Packages not named
main are ignored.
`,
}

View File

@ -12,6 +12,7 @@
package main
import (
"crypto/tls"
"fmt"
"io"
"io/ioutil"
@ -24,8 +25,17 @@ import (
// httpClient is the default HTTP client, but a variable so it can be
// changed by tests, without modifying http.DefaultClient.
var httpClient = http.DefaultClient
var impatientHTTPClient = &http.Client{
// impatientInsecureHTTPClient is used in -insecure mode,
// when we're connecting to https servers that might not be there
// or might be using self-signed certificates.
var impatientInsecureHTTPClient = &http.Client{
Timeout: time.Duration(5 * time.Second),
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
type httpError struct {
@ -71,7 +81,7 @@ func httpsOrHTTP(importPath string, security securityMode) (urlStr string, body
log.Printf("Fetching %s", urlStr)
}
if security == insecure && scheme == "https" { // fail earlier
res, err = impatientHTTPClient.Get(urlStr)
res, err = impatientInsecureHTTPClient.Get(urlStr)
} else {
res, err = httpClient.Get(urlStr)
}
@ -83,16 +93,12 @@ func httpsOrHTTP(importPath string, security securityMode) (urlStr string, body
}
}
urlStr, res, err := fetch("https")
if err != nil || res.StatusCode != 200 {
if err != nil {
if buildV {
if err != nil {
log.Printf("https fetch failed.")
} else {
log.Printf("ignoring https fetch with status code %d", res.StatusCode)
}
log.Printf("https fetch failed: %v", err)
}
closeBody(res)
if security == insecure {
closeBody(res)
urlStr, res, err = fetch("http")
}
}

View File

@ -78,6 +78,14 @@ syntax of package template. The default output is equivalent to -f
XTestImports []string // imports from XTestGoFiles
}
The error information, if any, is
type PackageError struct {
ImportStack []string // shortest path from package named on command line to this one
Pos string // position of error (if present, file:line:col)
Err string // the error itself
}
The template function "join" calls strings.Join.
The template function "context" returns the build context, defined as:

View File

@ -285,8 +285,8 @@ func printUsage(w io.Writer) {
func usage() {
// special case "go test -h"
if len(os.Args) > 1 && os.Args[1] == "test" {
os.Stdout.WriteString(testUsage + "\n\n" +
strings.TrimSpace(testFlag1) + "\n\n" +
os.Stderr.WriteString(testUsage + "\n\n" +
strings.TrimSpace(testFlag1) + "\n\n\t" +
strings.TrimSpace(testFlag2) + "\n")
os.Exit(2)
}
@ -353,7 +353,7 @@ func importPathsNoDotExpansion(args []string) []string {
} else {
a = path.Clean(a)
}
if a == "all" || a == "std" || a == "cmd" {
if isMetaPackage(a) {
out = append(out, allPackages(a)...)
continue
}
@ -554,7 +554,7 @@ func allPackages(pattern string) []string {
func matchPackages(pattern string) []string {
match := func(string) bool { return true }
treeCanMatch := func(string) bool { return true }
if pattern != "all" && pattern != "std" && pattern != "cmd" {
if !isMetaPackage(pattern) {
match = matchPattern(pattern)
treeCanMatch = treeCanMatchPattern(pattern)
}
@ -588,10 +588,9 @@ func matchPackages(pattern string) []string {
}
name := filepath.ToSlash(path[len(src):])
if pattern == "std" && (strings.Contains(name, ".") || name == "cmd") {
if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") {
// The name "std" is only the standard library.
// If the name has a dot, assume it's a domain name for go get,
// and if the name is cmd, it's the root of the command tree.
// If the name is cmd, it's the root of the command tree.
return filepath.SkipDir
}
if !treeCanMatch(name) {
@ -674,7 +673,14 @@ func matchPackagesInFS(pattern string) []string {
if !match(name) {
return nil
}
if _, err = build.ImportDir(path, 0); err != 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 := buildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) {
if _, noGo := err.(*build.NoGoError); !noGo {
log.Print(err)
}

View File

@ -7,6 +7,7 @@ package main
import (
"bytes"
"debug/elf"
"debug/macho"
"encoding/binary"
"fmt"
"io"
@ -69,11 +70,11 @@ func readELFNote(filename, name string, typ int32) ([]byte, error) {
var elfGoNote = []byte("Go\x00\x00")
// readELFGoBuildID the Go build ID string from an ELF binary.
// The Go build ID is stored in a note described by an ELF PT_NOTE prog header.
// The caller has already opened filename, to get f, and read the first 4 kB out, in data.
// The Go build ID is stored in a note described by an ELF PT_NOTE prog
// header. The caller has already opened filename, to get f, and read
// at least 4 kB out, in data.
func readELFGoBuildID(filename string, f *os.File, data []byte) (buildid string, err error) {
// Assume the note content is in the first 4 kB, already read.
// Assume the note content is in the data, already read.
// Rewrite the ELF header to set shnum to 0, so that we can pass
// the data to elf.NewFile and it will decode the Prog list but not
// try to read the section headers and the string table from disk.
@ -95,22 +96,92 @@ func readELFGoBuildID(filename string, f *os.File, data []byte) (buildid string,
return "", &os.PathError{Path: filename, Op: "parse", Err: err}
}
for _, p := range ef.Progs {
if p.Type != elf.PT_NOTE || p.Off >= uint64(len(data)) || p.Off+p.Filesz >= uint64(len(data)) || p.Filesz < 16 {
if p.Type != elf.PT_NOTE || p.Filesz < 16 {
continue
}
note := data[p.Off : p.Off+p.Filesz]
nameSize := ef.ByteOrder.Uint32(note)
valSize := ef.ByteOrder.Uint32(note[4:])
tag := ef.ByteOrder.Uint32(note[8:])
name := note[12:16]
if nameSize != 4 || 16+valSize > uint32(len(note)) || tag != elfGoBuildIDTag || !bytes.Equal(name, elfGoNote) {
continue
var note []byte
if p.Off+p.Filesz < uint64(len(data)) {
note = data[p.Off : p.Off+p.Filesz]
} else {
// For some linkers, such as the Solaris linker,
// the buildid may not be found in data (which
// likely contains the first 16kB of the file)
// or even the first few megabytes of the file
// due to differences in note segment placement;
// in that case, extract the note data manually.
_, err = f.Seek(int64(p.Off), 0)
if err != nil {
return "", err
}
note = make([]byte, p.Filesz)
_, err = io.ReadFull(f, note)
if err != nil {
return "", err
}
}
return string(note[16 : 16+valSize]), nil
filesz := p.Filesz
for filesz >= 16 {
nameSize := ef.ByteOrder.Uint32(note)
valSize := ef.ByteOrder.Uint32(note[4:])
tag := ef.ByteOrder.Uint32(note[8:])
name := note[12:16]
if nameSize == 4 && 16+valSize <= uint32(len(note)) && tag == elfGoBuildIDTag && bytes.Equal(name, elfGoNote) {
return string(note[16 : 16+valSize]), nil
}
nameSize = (nameSize + 3) &^ 3
valSize = (valSize + 3) &^ 3
notesz := uint64(12 + nameSize + valSize)
if filesz <= notesz {
break
}
filesz -= notesz
note = note[notesz:]
}
}
// No note. Treat as successful but build ID empty.
return "", nil
}
// The Go build ID is stored at the beginning of the Mach-O __text segment.
// The caller has already opened filename, to get f, and read a few kB out, in data.
// Sadly, that's not guaranteed to hold the note, because there is an arbitrary amount
// of other junk placed in the file ahead of the main text.
func readMachoGoBuildID(filename string, f *os.File, data []byte) (buildid string, err error) {
// If the data we want has already been read, don't worry about Mach-O parsing.
// This is both an optimization and a hedge against the Mach-O parsing failing
// in the future due to, for example, the name of the __text section changing.
if b, err := readRawGoBuildID(filename, data); b != "" && err == nil {
return b, err
}
mf, err := macho.NewFile(f)
if err != nil {
return "", &os.PathError{Path: filename, Op: "parse", Err: err}
}
sect := mf.Section("__text")
if sect == nil {
// Every binary has a __text section. Something is wrong.
return "", &os.PathError{Path: filename, Op: "parse", Err: fmt.Errorf("cannot find __text section")}
}
// It should be in the first few bytes, but read a lot just in case,
// especially given our past problems on OS X with the build ID moving.
// There shouldn't be much difference between reading 4kB and 32kB:
// the hard part is getting to the data, not transferring it.
n := sect.Size
if n > uint64(BuildIDReadSize) {
n = uint64(BuildIDReadSize)
}
buf := make([]byte, n)
if _, err := f.ReadAt(buf, int64(sect.Offset)); err != nil {
return "", err
}
return readRawGoBuildID(filename, buf)
}

View File

@ -6,11 +6,29 @@ package main_test
import (
main "cmd/go"
"go/build"
"runtime"
"testing"
)
func TestNoteReading(t *testing.T) {
testNoteReading(t)
}
func TestNoteReading2K(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skipf("2kB is not enough on %s", runtime.GOOS)
}
// Set BuildIDReadSize to 2kB to exercise Mach-O parsing more strictly.
defer func(old int) {
main.BuildIDReadSize = old
}(main.BuildIDReadSize)
main.BuildIDReadSize = 2 * 1024
testNoteReading(t)
}
func testNoteReading(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.tempFile("hello.go", `package main; func main() { print("hello, world\n") }`)
@ -24,26 +42,25 @@ func TestNoteReading(t *testing.T) {
t.Fatalf("buildID in hello binary = %q, want %q", id, buildID)
}
if runtime.GOOS == "linux" && runtime.GOARCH == "ppc64le" {
t.Skipf("skipping - golang.org/issue/11184")
switch {
case !build.Default.CgoEnabled:
t.Skipf("skipping - no cgo, so assuming external linking not available")
case runtime.GOOS == "linux" && (runtime.GOARCH == "ppc64le" || runtime.GOARCH == "ppc64"):
t.Skipf("skipping - external linking not supported, golang.org/issue/11184")
case runtime.GOOS == "linux" && (runtime.GOARCH == "mips64le" || runtime.GOARCH == "mips64"):
t.Skipf("skipping - external linking not supported, golang.org/issue/12560")
case runtime.GOOS == "openbsd" && runtime.GOARCH == "arm":
t.Skipf("skipping - external linking not supported, golang.org/issue/10619")
case runtime.GOOS == "plan9":
t.Skipf("skipping - external linking not supported")
}
switch runtime.GOOS {
case "plan9":
// no external linking
t.Logf("no external linking - skipping linkmode=external test")
case "solaris":
t.Logf("skipping - golang.org/issue/12178")
default:
tg.run("build", "-ldflags", "-buildid="+buildID+" -linkmode=external", "-o", tg.path("hello.exe"), tg.path("hello.go"))
id, err := main.ReadBuildIDFromBinary(tg.path("hello.exe"))
if err != nil {
t.Fatalf("reading build ID from hello binary (linkmode=external): %v", err)
}
if id != buildID {
t.Fatalf("buildID in hello binary = %q, want %q (linkmode=external)", id, buildID)
}
tg.run("build", "-ldflags", "-buildid="+buildID+" -linkmode=external", "-o", tg.path("hello.exe"), tg.path("hello.go"))
id, err = main.ReadBuildIDFromBinary(tg.path("hello.exe"))
if err != nil {
t.Fatalf("reading build ID from hello binary (linkmode=external): %v", err)
}
if id != buildID {
t.Fatalf("buildID in hello binary = %q, want %q (linkmode=external)", id, buildID)
}
}

View File

@ -118,7 +118,7 @@ func (p *Package) vendored(imports []string) []string {
seen := make(map[string]bool)
var all []string
for _, path := range imports {
path, _ = vendoredImportPath(p, path)
path = vendoredImportPath(p, path)
if !seen[path] {
seen[path] = true
all = append(all, path)
@ -156,7 +156,7 @@ func (p *Package) copyBuild(pp *build.Package) {
if buildContext.Compiler == "gccgo" {
p.Standard = stdpkg[p.ImportPath]
} else {
p.Standard = p.Goroot && p.ImportPath != "" && !strings.Contains(p.ImportPath, ".")
p.Standard = p.Goroot && p.ImportPath != "" && isStandardImportPath(p.ImportPath)
}
p.GoFiles = pp.GoFiles
p.CgoFiles = pp.CgoFiles
@ -181,6 +181,19 @@ func (p *Package) copyBuild(pp *build.Package) {
p.XTestImports = pp.XTestImports
}
// isStandardImportPath reports whether $GOROOT/src/path should be considered
// part of the standard distribution. For historical reasons we allow people to add
// their own code to $GOROOT instead of using $GOPATH, but we assume that
// code will start with a domain name (dot in the first element).
func isStandardImportPath(path string) bool {
i := strings.Index(path, "/")
if i < 0 {
i = len(path)
}
elem := path[:i]
return !strings.Contains(elem, ".")
}
// A PackageError describes an error loading information about a package.
type PackageError struct {
ImportStack []string // shortest path from package named on command line to this one
@ -254,11 +267,14 @@ func reloadPackage(arg string, stk *importStack) *Package {
return loadPackage(arg, stk)
}
// The Go 1.5 vendoring experiment is enabled by setting GO15VENDOREXPERIMENT=1.
// The Go 1.5 vendoring experiment was enabled by setting GO15VENDOREXPERIMENT=1.
// In Go 1.6 this is on by default and is disabled by setting GO15VENDOREXPERIMENT=0.
// In Go 1.7 the variable will stop having any effect.
// The variable is obnoxiously long so that years from now when people find it in
// their profiles and wonder what it does, there is some chance that a web search
// might answer the question.
var go15VendorExperiment = os.Getenv("GO15VENDOREXPERIMENT") == "1"
// There is a copy of this variable in src/go/build/build.go. Delete that one when this one goes away.
var go15VendorExperiment = os.Getenv("GO15VENDOREXPERIMENT") != "0"
// dirToImportPath returns the pseudo-import path we use for a package
// outside the Go path. It begins with _/ and then contains the full path
@ -314,11 +330,14 @@ func loadImport(path, srcDir string, parent *Package, stk *importStack, importPo
importPath := path
origPath := path
isLocal := build.IsLocalImport(path)
var vendorSearch []string
if isLocal {
importPath = dirToImportPath(filepath.Join(srcDir, path))
} else if mode&useVendor != 0 {
path, vendorSearch = vendoredImportPath(parent, path)
// We do our own vendor resolution, because we want to
// find out the key to use in packageCache without the
// overhead of repeated calls to buildContext.Import.
// The code is also needed in a few other places anyway.
path = vendoredImportPath(parent, path)
importPath = path
}
@ -345,29 +364,12 @@ func loadImport(path, srcDir string, parent *Package, stk *importStack, importPo
//
// TODO: After Go 1, decide when to pass build.AllowBinary here.
// See issue 3268 for mistakes to avoid.
bp, err := buildContext.Import(path, srcDir, build.ImportComment)
// If we got an error from go/build about package not found,
// it contains the directories from $GOROOT and $GOPATH that
// were searched. Add to that message the vendor directories
// that were searched.
if err != nil && len(vendorSearch) > 0 {
// NOTE(rsc): The direct text manipulation here is fairly awful,
// but it avoids defining new go/build API (an exported error type)
// late in the Go 1.5 release cycle. If this turns out to be a more general
// problem we could define a real error type when the decision can be
// considered more carefully.
text := err.Error()
if strings.Contains(text, "cannot find package \"") && strings.Contains(text, "\" in any of:\n\t") {
old := strings.SplitAfter(text, "\n")
lines := []string{old[0]}
for _, dir := range vendorSearch {
lines = append(lines, "\t"+dir+" (vendor tree)\n")
}
lines = append(lines, old[1:]...)
err = errors.New(strings.Join(lines, ""))
}
buildMode := build.ImportComment
if !go15VendorExperiment || mode&useVendor == 0 || path != origPath {
// Not vendoring, or we already found the vendored path.
buildMode |= build.IgnoreVendor
}
bp, err := buildContext.Import(path, srcDir, buildMode)
bp.ImportPath = importPath
if gobin != "" {
bp.BinDir = gobin
@ -377,7 +379,7 @@ func loadImport(path, srcDir string, parent *Package, stk *importStack, importPo
err = fmt.Errorf("code in directory %s expects import %q", bp.Dir, bp.ImportComment)
}
p.load(stk, bp, err)
if p.Error != nil && len(importPos) > 0 {
if p.Error != nil && p.Error.Pos == "" && len(importPos) > 0 {
pos := importPos[0]
pos.Filename = shortPath(pos.Filename)
p.Error.Pos = pos.String()
@ -411,14 +413,11 @@ func isDir(path string) bool {
// vendoredImportPath returns the expansion of path when it appears in parent.
// If parent is x/y/z, then path might expand to x/y/z/vendor/path, x/y/vendor/path,
// x/vendor/path, vendor/path, or else stay x/y/z if none of those exist.
// x/vendor/path, vendor/path, or else stay path if none of those exist.
// vendoredImportPath returns the expanded path or, if no expansion is found, the original.
// If no expansion is found, vendoredImportPath also returns a list of vendor directories
// it searched along the way, to help prepare a useful error message should path turn
// out not to exist.
func vendoredImportPath(parent *Package, path string) (found string, searched []string) {
func vendoredImportPath(parent *Package, path string) (found string) {
if parent == nil || parent.Root == "" || !go15VendorExperiment {
return path, nil
return path
}
dir := filepath.Clean(parent.Dir)
root := filepath.Join(parent.Root, "src")
@ -438,7 +437,7 @@ func vendoredImportPath(parent *Package, path string) (found string, searched []
continue
}
targ := filepath.Join(dir[:i], vpath)
if isDir(targ) {
if isDir(targ) && hasGoFiles(targ) {
// We started with parent's dir c:\gopath\src\foo\bar\baz\quux\xyzzy.
// We know the import path for parent's dir.
// We chopped off some number of path elements and
@ -453,14 +452,26 @@ func vendoredImportPath(parent *Package, path string) (found string, searched []
// and found c:\gopath\src\vendor\path.
// We chopped \foo\bar (length 8) but the import path is "foo/bar" (length 7).
// Use "vendor/path" without any prefix.
return vpath, nil
return vpath
}
return parent.ImportPath[:len(parent.ImportPath)-chopped] + "/" + vpath, nil
return parent.ImportPath[:len(parent.ImportPath)-chopped] + "/" + vpath
}
// Note the existence of a vendor directory in case path is not found anywhere.
searched = append(searched, targ)
}
return path, searched
return path
}
// hasGoFiles reports whether dir contains any files with names ending in .go.
// For a vendor check we must exclude directories that contain no .go files.
// Otherwise it is not possible to vendor just a/b/c and still import the
// non-vendored a/b. See golang.org/issue/13832.
func hasGoFiles(dir string) bool {
fis, _ := ioutil.ReadDir(dir)
for _, fi := range fis {
if !fi.IsDir() && strings.HasSuffix(fi.Name(), ".go") {
return true
}
}
return false
}
// reusePackage reuses package p to satisfy the import at the top
@ -522,7 +533,7 @@ func disallowInternal(srcDir string, p *Package, stk *importStack) *Package {
i-- // rewind over slash in ".../internal"
}
parent := p.Dir[:i+len(p.Dir)-len(p.ImportPath)]
if hasPathPrefix(filepath.ToSlash(srcDir), filepath.ToSlash(parent)) {
if hasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) {
return p
}
@ -619,7 +630,7 @@ func disallowVendorVisibility(srcDir string, p *Package, stk *importStack) *Pack
return p
}
parent := p.Dir[:truncateTo]
if hasPathPrefix(filepath.ToSlash(srcDir), filepath.ToSlash(parent)) {
if hasFilePathPrefix(filepath.Clean(srcDir), filepath.Clean(parent)) {
return p
}
@ -712,6 +723,7 @@ func expandScanner(err error) error {
var raceExclude = map[string]bool{
"runtime/race": true,
"runtime/msan": true,
"runtime/cgo": true,
"cmd/cgo": true,
"syscall": true,
@ -725,6 +737,7 @@ var cgoExclude = map[string]bool{
var cgoSyscallExclude = map[string]bool{
"runtime/cgo": true,
"runtime/race": true,
"runtime/msan": true,
}
// load populates p using information from bp, err, which should
@ -829,27 +842,40 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
importPaths = append(importPaths, "syscall")
}
// Currently build mode c-shared, or -linkshared, forces
// Currently build modes c-shared, pie, and -linkshared force
// external linking mode, and external linking mode forces an
// import of runtime/cgo.
if p.Name == "main" && !p.Goroot && (buildBuildmode == "c-shared" || buildLinkshared) {
if p.Name == "main" && !p.Goroot && (buildBuildmode == "c-shared" || buildBuildmode == "pie" || buildLinkshared) {
importPaths = append(importPaths, "runtime/cgo")
}
// Everything depends on runtime, except runtime and unsafe.
if !p.Standard || (p.ImportPath != "runtime" && p.ImportPath != "unsafe") {
// Everything depends on runtime, except runtime, its internal
// subpackages, and unsafe.
if !p.Standard || (p.ImportPath != "runtime" && !strings.HasPrefix(p.ImportPath, "runtime/internal/") && p.ImportPath != "unsafe") {
importPaths = append(importPaths, "runtime")
// When race detection enabled everything depends on runtime/race.
// Exclude certain packages to avoid circular dependencies.
if buildRace && (!p.Standard || !raceExclude[p.ImportPath]) {
importPaths = append(importPaths, "runtime/race")
}
// MSan uses runtime/msan.
if buildMSan && (!p.Standard || !raceExclude[p.ImportPath]) {
importPaths = append(importPaths, "runtime/msan")
}
// On ARM with GOARM=5, everything depends on math for the link.
if p.Name == "main" && goarch == "arm" {
importPaths = append(importPaths, "math")
}
}
// Runtime and its internal packages depend on runtime/internal/sys,
// so that they pick up the generated zversion.go file.
// This can be an issue particularly for runtime/internal/atomic;
// see issue 13655.
if p.Standard && (p.ImportPath == "runtime" || strings.HasPrefix(p.ImportPath, "runtime/internal/")) && p.ImportPath != "runtime/internal/sys" {
importPaths = append(importPaths, "runtime/internal/sys")
}
// Build list of full paths to all Go files in the package,
// for use by commands like go fmt.
p.gofiles = stringList(p.GoFiles, p.CgoFiles, p.TestGoFiles, p.XTestGoFiles)
@ -931,6 +957,17 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
}
}
}
if p.Standard && !p1.Standard && p.Error == nil {
p.Error = &PackageError{
ImportStack: stk.copy(),
Err: fmt.Sprintf("non-standard import %q in standard package %q", path, p.ImportPath),
}
pos := p.build.ImportPos[path]
if len(pos) > 0 {
p.Error.Pos = pos[0].String()
}
}
path = p1.ImportPath
importPaths[i] = path
if i < len(p.Imports) {
@ -1613,15 +1650,24 @@ func packagesAndErrors(args []string) []*Package {
}
args = importPaths(args)
var pkgs []*Package
var stk importStack
var set = make(map[string]bool)
var (
pkgs []*Package
stk importStack
seenArg = make(map[string]bool)
seenPkg = make(map[*Package]bool)
)
for _, arg := range args {
if !set[arg] {
pkgs = append(pkgs, loadPackage(arg, &stk))
set[arg] = true
if seenArg[arg] {
continue
}
seenArg[arg] = true
pkg := loadPackage(arg, &stk)
if seenPkg[pkg] {
continue
}
seenPkg[pkg] = true
pkgs = append(pkgs, pkg)
}
computeStale(pkgs...)
@ -1792,8 +1838,17 @@ var (
goBuildEnd = []byte("\"\n \xff")
elfPrefix = []byte("\x7fELF")
machoPrefixes = [][]byte{
{0xfe, 0xed, 0xfa, 0xce},
{0xfe, 0xed, 0xfa, 0xcf},
{0xce, 0xfa, 0xed, 0xfe},
{0xcf, 0xfa, 0xed, 0xfe},
}
)
var BuildIDReadSize = 32 * 1024 // changed for testing
// ReadBuildIDFromBinary reads the build ID from a binary.
//
// ELF binaries store the build ID in a proper PT_NOTE section.
@ -1808,10 +1863,11 @@ func ReadBuildIDFromBinary(filename string) (id string, err error) {
return "", &os.PathError{Op: "parse", Path: filename, Err: errBuildIDUnknown}
}
// Read the first 16 kB of the binary file.
// Read the first 32 kB of the binary file.
// That should be enough to find the build ID.
// In ELF files, the build ID is in the leading headers,
// which are typically less than 4 kB, not to mention 16 kB.
// which are typically less than 4 kB, not to mention 32 kB.
// In Mach-O files, there's no limit, so we have to parse the file.
// On other systems, we're trying to read enough that
// we get the beginning of the text segment in the read.
// The offset where the text segment begins in a hello
@ -1819,7 +1875,6 @@ func ReadBuildIDFromBinary(filename string) (id string, err error) {
//
// Plan 9: 0x20
// Windows: 0x600
// Mach-O: 0x2000
//
f, err := os.Open(filename)
if err != nil {
@ -1827,7 +1882,7 @@ func ReadBuildIDFromBinary(filename string) (id string, err error) {
}
defer f.Close()
data := make([]byte, 16*1024)
data := make([]byte, BuildIDReadSize)
_, err = io.ReadFull(f, data)
if err == io.ErrUnexpectedEOF {
err = nil
@ -1839,7 +1894,17 @@ func ReadBuildIDFromBinary(filename string) (id string, err error) {
if bytes.HasPrefix(data, elfPrefix) {
return readELFGoBuildID(filename, f, data)
}
for _, m := range machoPrefixes {
if bytes.HasPrefix(data, m) {
return readMachoGoBuildID(filename, f, data)
}
}
return readRawGoBuildID(filename, data)
}
// readRawGoBuildID finds the raw build ID stored in text segment data.
func readRawGoBuildID(filename string, data []byte) (id string, err error) {
i := bytes.Index(data, goBuildPrefix)
if i < 0 {
// Missing. Treat as successful but build ID empty.

View File

@ -5,6 +5,9 @@
package main
import (
"io/ioutil"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
@ -57,6 +60,15 @@ var parseMetaGoImportsTests = []struct {
<body>`,
[]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">`,
[]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>`,
[]metaImport{{"chitin.io/chitin", "git", "https://github.com/chitin-io/chitin"}},
},
}
func TestParseMetaGoImports(t *testing.T) {
@ -71,3 +83,109 @@ func TestParseMetaGoImports(t *testing.T) {
}
}
}
func TestSharedLibName(t *testing.T) {
// TODO(avdva) - make these values platform-specific
prefix := "lib"
suffix := ".so"
testData := []struct {
args []string
pkgs []*Package
expected string
expectErr bool
rootedAt string
}{
{
args: []string{"std"},
pkgs: []*Package{},
expected: "std",
},
{
args: []string{"std", "cmd"},
pkgs: []*Package{},
expected: "std,cmd",
},
{
args: []string{},
pkgs: []*Package{&Package{ImportPath: "gopkg.in/somelib"}},
expected: "gopkg.in-somelib",
},
{
args: []string{"./..."},
pkgs: []*Package{&Package{ImportPath: "somelib"}},
expected: "somelib",
rootedAt: "somelib",
},
{
args: []string{"../somelib", "../somelib"},
pkgs: []*Package{&Package{ImportPath: "somelib"}},
expected: "somelib",
},
{
args: []string{"../lib1", "../lib2"},
pkgs: []*Package{&Package{ImportPath: "gopkg.in/lib1"}, &Package{ImportPath: "gopkg.in/lib2"}},
expected: "gopkg.in-lib1,gopkg.in-lib2",
},
{
args: []string{"./..."},
pkgs: []*Package{
&Package{ImportPath: "gopkg.in/dir/lib1"},
&Package{ImportPath: "gopkg.in/lib2"},
&Package{ImportPath: "gopkg.in/lib3"},
},
expected: "gopkg.in",
rootedAt: "gopkg.in",
},
{
args: []string{"std", "../lib2"},
pkgs: []*Package{},
expectErr: true,
},
{
args: []string{"all", "./"},
pkgs: []*Package{},
expectErr: true,
},
{
args: []string{"cmd", "fmt"},
pkgs: []*Package{},
expectErr: true,
},
}
for _, data := range testData {
func() {
if data.rootedAt != "" {
tmpGopath, err := ioutil.TempDir("", "gopath")
if err != nil {
t.Fatal(err)
}
oldGopath := buildContext.GOPATH
defer func() {
os.RemoveAll(tmpGopath)
buildContext.GOPATH = oldGopath
os.Chdir(cwd)
}()
root := filepath.Join(tmpGopath, "src", data.rootedAt)
err = os.MkdirAll(root, 0755)
if err != nil {
t.Fatal(err)
}
buildContext.GOPATH = tmpGopath
os.Chdir(root)
}
computed, err := libname(data.args, data.pkgs)
if err != nil {
if !data.expectErr {
t.Errorf("libname returned an error %q, expected a name", err.Error())
}
} else if data.expectErr {
t.Errorf("libname returned %q, expected an error", computed)
} else {
expected := prefix + data.expected + suffix
if expected != computed {
t.Errorf("libname returned %q, expected %q", computed, expected)
}
}
}()
}
}

View File

@ -64,7 +64,7 @@ func printStderr(args ...interface{}) (int, error) {
}
func runRun(cmd *Command, args []string) {
raceInit()
instrumentInit()
buildModeInit()
var b builder
b.init()
@ -89,8 +89,18 @@ func runRun(cmd *Command, args []string) {
fatalf("%s", p.Error)
}
p.omitDWARF = true
for _, err := range p.DepsErrors {
errorf("%s", err)
if len(p.DepsErrors) > 0 {
// Since these are errors in dependencies,
// the same error might show up multiple times,
// once in each package that depends on it.
// Only print each once.
printed := map[*PackageError]bool{}
for _, err := range p.DepsErrors {
if !printed[err] {
printed[err] = true
errorf("%s", err)
}
}
}
exitIfErrors()
if p.Name != "main" {

View File

@ -13,7 +13,6 @@ import (
"go/doc"
"go/parser"
"go/token"
"log"
"os"
"os/exec"
"path"
@ -33,7 +32,7 @@ func init() {
cmdTest.Run = runTest
}
const testUsage = "test [-c] [-i] [build and test flags] [packages] [flags for test binary]"
const testUsage = "test [build/test flags] [packages] [build/test flags & test binary flags]"
var cmdTest = &Command{
CustomFlags: true,
@ -68,11 +67,6 @@ non-test installation.
` + strings.TrimSpace(testFlag1) + ` See 'go help testflag' for details.
If the test binary needs any other flags, they should be presented after the
package names. The go tool treats as a flag the first argument that begins with
a minus sign that it does not recognize itself; that argument and all subsequent
arguments are passed as arguments to the test binary.
For more about build flags, see 'go help build'.
For more about specifying packages, see 'go help packages'.
@ -83,10 +77,16 @@ See also: go build, go vet.
const testFlag1 = `
In addition to the build flags, the flags handled by 'go test' itself are:
-args
Pass the remainder of the command line (everything after -args)
to the test binary, uninterpreted and unchanged.
Because this flag consumes the remainder of the command line,
the package list (if present) must appear before this flag.
-c
Compile the test binary to pkg.test but do not run it
(where pkg is the last element of the package's import path).
The file name can be changed with the -o flag.
Compile the test binary to pkg.test but do not run it
(where pkg is the last element of the package's import path).
The file name can be changed with the -o flag.
-exec xprog
Run the test binary using xprog. The behavior is the same as
@ -97,8 +97,8 @@ In addition to the build flags, the flags handled by 'go test' itself are:
Do not run the test.
-o file
Compile the test binary to the named file.
The test still runs (unless -c or -i is specified).
Compile the test binary to the named file.
The test still runs (unless -c or -i is specified).
The test binary also accepts flags that control execution of the test; these
flags are also accessible by 'go test'.
@ -207,6 +207,10 @@ const testFlag2 = `
Allow parallel execution of test functions that call t.Parallel.
The value of this flag is the maximum number of tests to run
simultaneously; by default, it is set to the value of GOMAXPROCS.
Note that -parallel only applies within a single test binary.
The 'go test' command may run tests for different packages
in parallel as well, according to the setting of the -p flag
(see 'go help build').
-run regexp
Run only those tests and examples matching the regular
@ -230,25 +234,63 @@ const testFlag2 = `
Verbose output: log all tests as they are run. Also print all
text from Log and Logf calls even if the test succeeds.
The test binary, called pkg.test where pkg is the name of the
directory containing the package sources, can be invoked directly
after building it with 'go test -c'. When invoking the test binary
directly, each of the standard flag names must be prefixed with 'test.',
as in -test.run=TestMyFunc or -test.v.
Each of these flags is also recognized with an optional 'test.' prefix,
as in -test.v. When invoking the generated test binary (the result of
'go test -c') directly, however, the prefix is mandatory.
When running 'go test', flags not listed above are passed through
unaltered. For instance, the command
The 'go test' command rewrites or removes recognized flags,
as appropriate, both before and after the optional package list,
before invoking the test binary.
go test -x -v -cpuprofile=prof.out -dir=testdata -update
For instance, the command
go test -v -myflag testdata -cpuprofile=prof.out -x
will compile the test binary and then run it as
pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update
pkg.test -test.v -myflag testdata -test.cpuprofile=prof.out
(The -x flag is removed because it applies only to the go command's
execution, not to the test itself.)
The test flags that generate profiles (other than for coverage) also
leave the test binary in pkg.test for use when analyzing the profiles.
Flags not recognized by 'go test' must be placed after any specified packages.
When 'go test' runs a test binary, it does so from within the
corresponding package's source code directory. Depending on the test,
it may be necessary to do the same when invoking a generated test
binary directly.
The command-line package list, if present, must appear before any
flag not known to the go test command. Continuing the example above,
the package list would have to appear before -myflag, but could appear
on either side of -v.
To keep an argument for a test binary from being interpreted as a
known flag or a package name, use -args (see 'go help test') which
passes the remainder of the command line through to the test binary
uninterpreted and unaltered.
For instance, the command
go test -v -args -x -v
will compile the test binary and then run it as
pkg.test -test.v -x -v
Similarly,
go test -args math
will compile the test binary and then run it as
pkg.test math
In the first example, the -x and the second -v are passed through to the
test binary unchanged and with no effect on the go command itself.
In the second example, the argument math is passed through to the test
binary, instead of being interpreted as the package list.
`
var helpTestfunc = &Command{
@ -328,7 +370,7 @@ func runTest(cmd *Command, args []string) {
findExecCmd() // initialize cached result
raceInit()
instrumentInit()
buildModeInit()
pkgs := packagesForBuild(pkgArgs)
if len(pkgs) == 0 {
@ -396,7 +438,7 @@ func runTest(cmd *Command, args []string) {
if deps["C"] {
delete(deps, "C")
deps["runtime/cgo"] = true
if goos == runtime.GOOS && goarch == runtime.GOARCH && !buildRace {
if goos == runtime.GOOS && goarch == runtime.GOARCH && !buildRace && !buildMSan {
deps["cmd/cgo"] = true
}
}
@ -442,7 +484,7 @@ func runTest(cmd *Command, args []string) {
}
for _, p := range testCoverPkgs {
if !used[p.ImportPath] {
log.Printf("warning: no packages being tested depend on %s", p.ImportPath)
fmt.Fprintf(os.Stderr, "warning: no packages being tested depend on %s\n", p.ImportPath)
}
}
@ -547,6 +589,9 @@ func runTest(cmd *Command, args []string) {
if buildRace {
extraOpts = "-race "
}
if buildMSan {
extraOpts = "-msan "
}
fmt.Fprintf(os.Stderr, "installing these packages with 'go test %s-i%s' will speed future tests.\n\n", extraOpts, args)
}
@ -829,10 +874,12 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
}
}
// writeTestmain writes _testmain.go. This must happen after recompileForTest,
// because recompileForTest modifies XXX.
if err := writeTestmain(filepath.Join(testDir, "_testmain.go"), t); err != nil {
return nil, nil, nil, err
if !buildN {
// writeTestmain writes _testmain.go. This must happen after recompileForTest,
// because recompileForTest modifies XXX.
if err := writeTestmain(filepath.Join(testDir, "_testmain.go"), t); err != nil {
return nil, nil, nil, err
}
}
computeStale(pmain)

16
libgo/go/cmd/go/testdata/flag_test.go vendored Normal file
View File

@ -0,0 +1,16 @@
package flag_test
import (
"flag"
"log"
"testing"
)
var v = flag.Int("v", 0, "v flag")
// Run this as go test pkg -v=7
func TestVFlagIsSet(t *testing.T) {
if *v != 7 {
log.Fatal("v flag not set")
}
}

View File

@ -0,0 +1,5 @@
package main
import _ "run/subdir/internal/private"
func main() {}

View File

@ -0,0 +1,5 @@
package main
import _ "run/internal"
func main() {}

View File

@ -0,0 +1 @@
package internal

View File

@ -0,0 +1 @@
package private

View File

@ -0,0 +1 @@
package dir1

View File

@ -7,6 +7,6 @@ import (
func TestMsgInternal(t *testing.T) {
if strings.Msg != "hello, world" {
t.Fatal("unexpected msg: %v", strings.Msg)
t.Fatalf("unexpected msg: %v", strings.Msg)
}
}

View File

@ -7,6 +7,6 @@ import (
func TestMsgExternal(t *testing.T) {
if strings.Msg != "hello, world" {
t.Fatal("unexpected msg: %v", strings.Msg)
t.Fatalf("unexpected msg: %v", strings.Msg)
}
}

View File

@ -0,0 +1 @@
package dir2

View File

@ -3,3 +3,5 @@ package x
import _ "p"
import _ "q"
import _ "r"
import _ "vend/dir1" // not vendored
import _ "vend/dir1/dir2" // vendored

View File

@ -87,6 +87,7 @@ func init() {
func testFlags(args []string) (packageNames, passToTest []string) {
inPkg := false
outputDir := ""
var explicitArgs []string
for i := 0; i < len(args); i++ {
if !strings.HasPrefix(args[i], "-") {
if !inPkg && packageNames == nil {
@ -114,6 +115,12 @@ func testFlags(args []string) (packageNames, passToTest []string) {
// make non-nil: we have seen the empty package list
packageNames = []string{}
}
if args[i] == "-args" || args[i] == "--args" {
// -args or --args signals that everything that follows
// should be passed to the test.
explicitArgs = args[i+1:]
break
}
passToTest = append(passToTest, args[i])
continue
}
@ -191,6 +198,8 @@ func testFlags(args []string) (packageNames, passToTest []string) {
}
passToTest = append(passToTest, "-test.outputdir", dir)
}
passToTest = append(passToTest, explicitArgs...)
return
}

View File

@ -104,6 +104,7 @@ func runTool(cmd *Command, args []string) {
fmt.Printf("%s\n", cmd)
return
}
args[0] = toolPath // in case the tool wants to re-exec itself, e.g. cmd/dist
toolCmd := &exec.Cmd{
Path: toolPath,
Args: args,

View File

@ -122,7 +122,7 @@ var vcsGit = &vcsCmd{
name: "Git",
cmd: "git",
createCmd: []string{"clone {repo} {dir}", "--git-dir={dir}/.git submodule update --init --recursive"},
createCmd: []string{"clone {repo} {dir}", "-C {dir} submodule update --init --recursive"},
downloadCmd: []string{"pull --ff-only", "submodule update --init --recursive"},
tagCmd: []tagCmd{
@ -137,8 +137,9 @@ var vcsGit = &vcsCmd{
// both createCmd and downloadCmd update the working dir.
// No need to do more here. We used to 'checkout master'
// but that doesn't work if the default branch is not named master.
// DO NOT add 'checkout master' here.
// See golang.org/issue/9032.
tagSyncDefault: []string{"checkout master", "submodule update --init --recursive"},
tagSyncDefault: []string{"submodule update --init --recursive"},
scheme: []string{"git", "https", "http", "git+ssh", "ssh"},
pingCmd: "ls-remote {scheme}://{repo}",
@ -385,9 +386,6 @@ func (v *vcsCmd) create(dir, repo string) error {
// download downloads any new changes for the repo in dir.
func (v *vcsCmd) download(dir string) error {
if err := v.fixDetachedHead(dir); err != nil {
return err
}
for _, cmd := range v.downloadCmd {
if !go15VendorExperiment && strings.Contains(cmd, "submodule") {
continue
@ -399,30 +397,6 @@ func (v *vcsCmd) download(dir string) error {
return nil
}
// fixDetachedHead switches a Git repository in dir from a detached head to the master branch.
// Go versions before 1.2 downloaded Git repositories in an unfortunate way
// that resulted in the working tree state being on a detached head.
// That meant the repository was not usable for normal Git operations.
// Go 1.2 fixed that, but we can't pull into a detached head, so if this is
// a Git repository we check for being on a detached head and switch to the
// real branch, almost always called "master".
// TODO(dsymonds): Consider removing this for Go 1.3.
func (v *vcsCmd) fixDetachedHead(dir string) error {
if v != vcsGit {
return nil
}
// "git symbolic-ref HEAD" succeeds iff we are not on a detached head.
if err := v.runVerboseOnly(dir, "symbolic-ref HEAD"); err == nil {
// not on a detached head
return nil
}
if buildV {
log.Printf("%s on detached head; repairing", dir)
}
return v.run(dir, "checkout master")
}
// tags returns the list of available tags for the repo in dir.
func (v *vcsCmd) tags(dir string) ([]string, error) {
var tags []string
@ -567,16 +541,8 @@ func repoRootForImportPath(importPath string, security securityMode) (*repoRoot,
lookup = lookup[:i]
}
rr, err = repoRootForImportDynamic(lookup, security)
// repoRootForImportDynamic returns error detail
// that is irrelevant if the user didn't intend to use a
// dynamic import in the first place.
// Squelch it.
if err != nil {
if buildV {
log.Printf("import %q: %v", importPath, err)
}
err = fmt.Errorf("unrecognized import path %q", importPath)
err = fmt.Errorf("unrecognized import path %q (%v)", importPath, err)
}
}
if err != nil {
@ -891,7 +857,7 @@ var vcsPaths = []*vcsPath{
// General syntax for any server.
// Must be last.
{
re: `^(?P<root>(?P<repo>([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\-]+)*$`,
re: `^(?P<root>(?P<repo>([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?(/~?[A-Za-z0-9_.\-]+)+?)\.(?P<vcs>bzr|git|hg|svn))(/~?[A-Za-z0-9_.\-]+)*$`,
ping: true,
},
}

View File

@ -18,14 +18,14 @@ func TestRepoRootForImportPath(t *testing.T) {
path string
want *repoRoot
}{
{
/*{
"code.google.com/p/go",
&repoRoot{
vcs: vcsHg,
repo: "https://code.google.com/p/go",
},
},
/*{
{
"code.google.com/r/go",
&repoRoot{
vcs: vcsHg,

View File

@ -24,12 +24,14 @@ func TestVendorImports(t *testing.T) {
tg.run("list", "-f", "{{.ImportPath}} {{.Imports}}", "vend/...")
want := `
vend [vend/vendor/p r]
vend/dir1 []
vend/hello [fmt vend/vendor/strings]
vend/subdir [vend/vendor/p r]
vend/vendor/p []
vend/vendor/q []
vend/vendor/strings []
vend/x [vend/x/vendor/p vend/vendor/q vend/x/vendor/r]
vend/vendor/vend/dir1/dir2 []
vend/x [vend/x/vendor/p vend/vendor/q vend/x/vendor/r vend/dir1 vend/vendor/vend/dir1/dir2]
vend/x/invalid [vend/x/invalid/vendor/foo]
vend/x/vendor/p []
vend/x/vendor/p/p [notfound]
@ -45,6 +47,14 @@ func TestVendorImports(t *testing.T) {
}
}
func TestVendorBuild(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.setenv("GO15VENDOREXPERIMENT", "1")
tg.run("build", "vend/x")
}
func TestVendorRun(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
@ -102,7 +112,7 @@ func TestVendorImportError(t *testing.T) {
re := regexp.MustCompile(`cannot find package "notfound" in any of:
.*[\\/]testdata[\\/]src[\\/]vend[\\/]x[\\/]vendor[\\/]notfound \(vendor tree\)
.*[\\/]testdata[\\/]src[\\/]vend[\\/]vendor[\\/]notfound \(vendor tree\)
.*[\\/]testdata[\\/]src[\\/]vend[\\/]vendor[\\/]notfound
.*[\\/]src[\\/]notfound \(from \$GOROOT\)
.*[\\/]testdata[\\/]src[\\/]notfound \(from \$GOPATH\)`)
@ -187,6 +197,18 @@ func TestVendorGetUpdate(t *testing.T) {
tg.run("get", "-u", "github.com/rsc/go-get-issue-11864")
}
func TestGetSubmodules(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
tg := testgo(t)
defer tg.cleanup()
tg.makeTempdir()
tg.setenv("GOPATH", tg.path("."))
tg.setenv("GO15VENDOREXPERIMENT", "1")
tg.run("get", "-d", "github.com/rsc/go-get-issue-12612")
tg.run("get", "-u", "-d", "github.com/rsc/go-get-issue-12612")
}
func TestVendorCache(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()

View File

@ -13,7 +13,6 @@ import (
"go/printer"
"go/scanner"
"go/token"
"internal/format"
"io"
"io/ioutil"
"os"
@ -88,7 +87,7 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error
return err
}
file, sourceAdj, indentAdj, err := format.Parse(fileSet, filename, src, stdin)
file, sourceAdj, indentAdj, err := parse(fileSet, filename, src, stdin)
if err != nil {
return err
}
@ -107,7 +106,7 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error
simplify(file)
}
res, err := format.Format(fileSet, file, sourceAdj, indentAdj, src, printer.Config{Mode: printerMode, Tabwidth: tabWidth})
res, err := format(fileSet, file, sourceAdj, indentAdj, src, printer.Config{Mode: printerMode, Tabwidth: tabWidth})
if err != nil {
return err
}

View File

@ -0,0 +1,176 @@
// 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.
// TODO(gri): This file and the file src/go/format/internal.go are
// the same (but for this comment and the package name). Do not modify
// one without the other. Determine if we can factor out functionality
// in a public API. See also #11844 for context.
package main
import (
"bytes"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"strings"
)
// parse parses src, which was read from the named file,
// as a Go source file, declaration, or statement list.
func parse(fset *token.FileSet, filename string, src []byte, fragmentOk bool) (
file *ast.File,
sourceAdj func(src []byte, indent int) []byte,
indentAdj int,
err error,
) {
// Try as whole source file.
file, err = parser.ParseFile(fset, filename, src, parserMode)
// If there's no error, return. If the error is that the source file didn't begin with a
// package line and source fragments are ok, fall through to
// try as a source fragment. Stop and return on any other error.
if err == nil || !fragmentOk || !strings.Contains(err.Error(), "expected 'package'") {
return
}
// If this is a declaration list, make it a source file
// by inserting a package clause.
// Insert using a ;, not a newline, so that the line numbers
// in psrc match the ones in src.
psrc := append([]byte("package p;"), src...)
file, err = parser.ParseFile(fset, filename, psrc, parserMode)
if err == nil {
sourceAdj = func(src []byte, indent int) []byte {
// Remove the package clause.
// Gofmt has turned the ; into a \n.
src = src[indent+len("package p\n"):]
return bytes.TrimSpace(src)
}
return
}
// If the error is that the source file didn't begin with a
// declaration, fall through to try as a statement list.
// Stop and return on any other error.
if !strings.Contains(err.Error(), "expected declaration") {
return
}
// If this is a statement list, make it a source file
// by inserting a package clause and turning the list
// into a function body. This handles expressions too.
// Insert using a ;, not a newline, so that the line numbers
// in fsrc match the ones in src. Add an extra '\n' before the '}'
// to make sure comments are flushed before the '}'.
fsrc := append(append([]byte("package p; func _() {"), src...), '\n', '\n', '}')
file, err = parser.ParseFile(fset, filename, fsrc, parserMode)
if err == nil {
sourceAdj = func(src []byte, indent int) []byte {
// Cap adjusted indent to zero.
if indent < 0 {
indent = 0
}
// Remove the wrapping.
// Gofmt has turned the ; into a \n\n.
// There will be two non-blank lines with indent, hence 2*indent.
src = src[2*indent+len("package p\n\nfunc _() {"):]
// Remove only the "}\n" suffix: remaining whitespaces will be trimmed anyway
src = src[:len(src)-len("}\n")]
return bytes.TrimSpace(src)
}
// Gofmt has also indented the function body one level.
// Adjust that with indentAdj.
indentAdj = -1
}
// Succeeded, or out of options.
return
}
// format formats the given package file originally obtained from src
// and adjusts the result based on the original source via sourceAdj
// and indentAdj.
func format(
fset *token.FileSet,
file *ast.File,
sourceAdj func(src []byte, indent int) []byte,
indentAdj int,
src []byte,
cfg printer.Config,
) ([]byte, error) {
if sourceAdj == nil {
// Complete source file.
var buf bytes.Buffer
err := cfg.Fprint(&buf, fset, file)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// Partial source file.
// Determine and prepend leading space.
i, j := 0, 0
for j < len(src) && isSpace(src[j]) {
if src[j] == '\n' {
i = j + 1 // byte offset of last line in leading space
}
j++
}
var res []byte
res = append(res, src[:i]...)
// Determine and prepend indentation of first code line.
// Spaces are ignored unless there are no tabs,
// in which case spaces count as one tab.
indent := 0
hasSpace := false
for _, b := range src[i:j] {
switch b {
case ' ':
hasSpace = true
case '\t':
indent++
}
}
if indent == 0 && hasSpace {
indent = 1
}
for i := 0; i < indent; i++ {
res = append(res, '\t')
}
// Format the source.
// Write it without any leading and trailing space.
cfg.Indent = indent + indentAdj
var buf bytes.Buffer
err := cfg.Fprint(&buf, fset, file)
if err != nil {
return nil, err
}
out := sourceAdj(buf.Bytes(), cfg.Indent)
// If the adjusted output is empty, the source
// was empty but (possibly) for white space.
// The result is the incoming source.
if len(out) == 0 {
return src, nil
}
// Otherwise, append output to leading space.
res = append(res, out...)
// Determine and append trailing space.
i = len(src)
for i > 0 && isSpace(src[i-1]) {
i--
}
return append(res, src[i:]...), nil
}
// isSpace reports whether the byte is a space character.
// isSpace defines a space as being among the following bytes: ' ', '\t', '\n' and '\r'.
func isSpace(b byte) bool {
return b == ' ' || b == '\t' || b == '\n' || b == '\r'
}

View File

@ -15,7 +15,6 @@ import (
"go/ast"
"go/printer"
"go/token"
"internal/format"
"io"
"os"
"path/filepath"
@ -33,7 +32,7 @@ var (
)
func gofmt(fset *token.FileSet, filename string, src *bytes.Buffer) error {
f, _, _, err := format.Parse(fset, filename, src.Bytes(), false)
f, _, _, err := parse(fset, filename, src.Bytes(), false)
if err != nil {
return err
}
@ -61,7 +60,7 @@ func testFile(t *testing.T, b1, b2 *bytes.Buffer, filename string) {
// exclude files w/ syntax errors (typically test cases)
fset := token.NewFileSet()
if _, _, _, err = format.Parse(fset, filename, b1.Bytes(), false); err != nil {
if _, _, _, err = parse(fset, filename, b1.Bytes(), false); err != nil {
if *verbose {
fmt.Fprintf(os.Stderr, "ignoring %s\n", err)
}

View File

@ -1,9 +0,0 @@
package P
func f() {
if x {
y
} else {
z
}
}

View File

@ -1,8 +0,0 @@
package P
func f() {
if x {
y
} else
z
}

View File

@ -77,14 +77,6 @@ func (br *bitReader) ReadBit() bool {
return n != 0
}
func (br *bitReader) TryReadBit() (bit byte, ok bool) {
if br.bits > 0 {
br.bits--
return byte(br.n>>br.bits) & 1, true
}
return 0, false
}
func (br *bitReader) Err() error {
return br.err
}

View File

@ -173,6 +173,7 @@ const rand3Hex = "1744b384d68c042371244e13500d4bfb98c6244e3d71a5b700224420b59c59
const (
digits = iota
twain
random
)
var testfiles = []string{
@ -180,8 +181,10 @@ var testfiles = []string{
// does not repeat, but there are only 10 possible digits, so it should be
// reasonably compressible.
digits: "testdata/e.txt.bz2",
// Twain is Project Gutenberg's edition of Mark Twain's classic English novel.
// Twain is Mark Twain's classic English novel.
twain: "testdata/Mark.Twain-Tom.Sawyer.txt.bz2",
// 16KB of random data from /dev/urandom
random: "testdata/random.data.bz2",
}
func benchmarkDecode(b *testing.B, testfile int) {
@ -198,6 +201,7 @@ func benchmarkDecode(b *testing.B, testfile int) {
func BenchmarkDecodeDigits(b *testing.B) { benchmarkDecode(b, digits) }
func BenchmarkDecodeTwain(b *testing.B) { benchmarkDecode(b, twain) }
func BenchmarkDecodeRand(b *testing.B) { benchmarkDecode(b, random) }
func TestBufferOverrun(t *testing.T) {
// Tests https://golang.org/issue/5747.

View File

@ -38,23 +38,35 @@ func (t *huffmanTree) Decode(br *bitReader) (v uint16) {
for {
node := &t.nodes[nodeIndex]
bit, ok := br.TryReadBit()
if !ok && br.ReadBit() {
bit = 1
}
// bzip2 encodes left as a true bit.
if bit != 0 {
// left
if node.left == invalidNodeValue {
return node.leftValue
}
nodeIndex = node.left
var bit uint16
if br.bits > 0 {
// Get next bit - fast path.
br.bits--
bit = 0 - (uint16(br.n>>br.bits) & 1)
} else {
// right
if node.right == invalidNodeValue {
return node.rightValue
}
nodeIndex = node.right
// Get next bit - slow path.
// Use ReadBits to retrieve a single bit
// from the underling io.ByteReader.
bit = 0 - uint16(br.ReadBits(1))
}
// now
// bit = 0xffff if the next bit was 1
// bit = 0x0000 if the next bit was 0
// 1 means left, 0 means right.
//
// if bit == 0xffff {
// nodeIndex = node.left
// } else {
// nodeIndex = node.right
// }
nodeIndex = (bit & node.left) | (^bit & node.right)
if nodeIndex == invalidNodeValue {
// We found a leaf. Use the value of bit to decide
// whether is a left or a right value.
return (bit & node.leftValue) | (^bit & node.rightValue)
}
}
}

View File

@ -7,6 +7,7 @@ package flate
import (
"bytes"
"fmt"
"internal/testenv"
"io"
"io/ioutil"
"reflect"
@ -343,6 +344,9 @@ var deflateInflateStringTests = []deflateInflateStringTest{
}
func TestDeflateInflateString(t *testing.T) {
if testing.Short() && testenv.Builder() == "" {
t.Skip("skipping in short mode")
}
for _, test := range deflateInflateStringTests {
gold, err := ioutil.ReadFile(test.filename)
if err != nil {
@ -436,7 +440,11 @@ func TestWriterReset(t *testing.T) {
t.Fatalf("NewWriter: %v", err)
}
buf := []byte("hello world")
for i := 0; i < 1024; i++ {
n := 1024
if testing.Short() {
n = 10
}
for i := 0; i < n; i++ {
w.Write(buf)
}
w.Reset(ioutil.Discard)

View File

@ -1,78 +0,0 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package flate
// autogenerated by go run gen.go -output fixedhuff.go, DO NOT EDIT
var fixedHuffmanDecoder = huffmanDecoder{
7,
[huffmanNumChunks]uint32{
0x1007, 0x0508, 0x0108, 0x1188, 0x1107, 0x0708, 0x0308, 0x0c09,
0x1087, 0x0608, 0x0208, 0x0a09, 0x0008, 0x0808, 0x0408, 0x0e09,
0x1047, 0x0588, 0x0188, 0x0909, 0x1147, 0x0788, 0x0388, 0x0d09,
0x10c7, 0x0688, 0x0288, 0x0b09, 0x0088, 0x0888, 0x0488, 0x0f09,
0x1027, 0x0548, 0x0148, 0x11c8, 0x1127, 0x0748, 0x0348, 0x0c89,
0x10a7, 0x0648, 0x0248, 0x0a89, 0x0048, 0x0848, 0x0448, 0x0e89,
0x1067, 0x05c8, 0x01c8, 0x0989, 0x1167, 0x07c8, 0x03c8, 0x0d89,
0x10e7, 0x06c8, 0x02c8, 0x0b89, 0x00c8, 0x08c8, 0x04c8, 0x0f89,
0x1017, 0x0528, 0x0128, 0x11a8, 0x1117, 0x0728, 0x0328, 0x0c49,
0x1097, 0x0628, 0x0228, 0x0a49, 0x0028, 0x0828, 0x0428, 0x0e49,
0x1057, 0x05a8, 0x01a8, 0x0949, 0x1157, 0x07a8, 0x03a8, 0x0d49,
0x10d7, 0x06a8, 0x02a8, 0x0b49, 0x00a8, 0x08a8, 0x04a8, 0x0f49,
0x1037, 0x0568, 0x0168, 0x11e8, 0x1137, 0x0768, 0x0368, 0x0cc9,
0x10b7, 0x0668, 0x0268, 0x0ac9, 0x0068, 0x0868, 0x0468, 0x0ec9,
0x1077, 0x05e8, 0x01e8, 0x09c9, 0x1177, 0x07e8, 0x03e8, 0x0dc9,
0x10f7, 0x06e8, 0x02e8, 0x0bc9, 0x00e8, 0x08e8, 0x04e8, 0x0fc9,
0x1007, 0x0518, 0x0118, 0x1198, 0x1107, 0x0718, 0x0318, 0x0c29,
0x1087, 0x0618, 0x0218, 0x0a29, 0x0018, 0x0818, 0x0418, 0x0e29,
0x1047, 0x0598, 0x0198, 0x0929, 0x1147, 0x0798, 0x0398, 0x0d29,
0x10c7, 0x0698, 0x0298, 0x0b29, 0x0098, 0x0898, 0x0498, 0x0f29,
0x1027, 0x0558, 0x0158, 0x11d8, 0x1127, 0x0758, 0x0358, 0x0ca9,
0x10a7, 0x0658, 0x0258, 0x0aa9, 0x0058, 0x0858, 0x0458, 0x0ea9,
0x1067, 0x05d8, 0x01d8, 0x09a9, 0x1167, 0x07d8, 0x03d8, 0x0da9,
0x10e7, 0x06d8, 0x02d8, 0x0ba9, 0x00d8, 0x08d8, 0x04d8, 0x0fa9,
0x1017, 0x0538, 0x0138, 0x11b8, 0x1117, 0x0738, 0x0338, 0x0c69,
0x1097, 0x0638, 0x0238, 0x0a69, 0x0038, 0x0838, 0x0438, 0x0e69,
0x1057, 0x05b8, 0x01b8, 0x0969, 0x1157, 0x07b8, 0x03b8, 0x0d69,
0x10d7, 0x06b8, 0x02b8, 0x0b69, 0x00b8, 0x08b8, 0x04b8, 0x0f69,
0x1037, 0x0578, 0x0178, 0x11f8, 0x1137, 0x0778, 0x0378, 0x0ce9,
0x10b7, 0x0678, 0x0278, 0x0ae9, 0x0078, 0x0878, 0x0478, 0x0ee9,
0x1077, 0x05f8, 0x01f8, 0x09e9, 0x1177, 0x07f8, 0x03f8, 0x0de9,
0x10f7, 0x06f8, 0x02f8, 0x0be9, 0x00f8, 0x08f8, 0x04f8, 0x0fe9,
0x1007, 0x0508, 0x0108, 0x1188, 0x1107, 0x0708, 0x0308, 0x0c19,
0x1087, 0x0608, 0x0208, 0x0a19, 0x0008, 0x0808, 0x0408, 0x0e19,
0x1047, 0x0588, 0x0188, 0x0919, 0x1147, 0x0788, 0x0388, 0x0d19,
0x10c7, 0x0688, 0x0288, 0x0b19, 0x0088, 0x0888, 0x0488, 0x0f19,
0x1027, 0x0548, 0x0148, 0x11c8, 0x1127, 0x0748, 0x0348, 0x0c99,
0x10a7, 0x0648, 0x0248, 0x0a99, 0x0048, 0x0848, 0x0448, 0x0e99,
0x1067, 0x05c8, 0x01c8, 0x0999, 0x1167, 0x07c8, 0x03c8, 0x0d99,
0x10e7, 0x06c8, 0x02c8, 0x0b99, 0x00c8, 0x08c8, 0x04c8, 0x0f99,
0x1017, 0x0528, 0x0128, 0x11a8, 0x1117, 0x0728, 0x0328, 0x0c59,
0x1097, 0x0628, 0x0228, 0x0a59, 0x0028, 0x0828, 0x0428, 0x0e59,
0x1057, 0x05a8, 0x01a8, 0x0959, 0x1157, 0x07a8, 0x03a8, 0x0d59,
0x10d7, 0x06a8, 0x02a8, 0x0b59, 0x00a8, 0x08a8, 0x04a8, 0x0f59,
0x1037, 0x0568, 0x0168, 0x11e8, 0x1137, 0x0768, 0x0368, 0x0cd9,
0x10b7, 0x0668, 0x0268, 0x0ad9, 0x0068, 0x0868, 0x0468, 0x0ed9,
0x1077, 0x05e8, 0x01e8, 0x09d9, 0x1177, 0x07e8, 0x03e8, 0x0dd9,
0x10f7, 0x06e8, 0x02e8, 0x0bd9, 0x00e8, 0x08e8, 0x04e8, 0x0fd9,
0x1007, 0x0518, 0x0118, 0x1198, 0x1107, 0x0718, 0x0318, 0x0c39,
0x1087, 0x0618, 0x0218, 0x0a39, 0x0018, 0x0818, 0x0418, 0x0e39,
0x1047, 0x0598, 0x0198, 0x0939, 0x1147, 0x0798, 0x0398, 0x0d39,
0x10c7, 0x0698, 0x0298, 0x0b39, 0x0098, 0x0898, 0x0498, 0x0f39,
0x1027, 0x0558, 0x0158, 0x11d8, 0x1127, 0x0758, 0x0358, 0x0cb9,
0x10a7, 0x0658, 0x0258, 0x0ab9, 0x0058, 0x0858, 0x0458, 0x0eb9,
0x1067, 0x05d8, 0x01d8, 0x09b9, 0x1167, 0x07d8, 0x03d8, 0x0db9,
0x10e7, 0x06d8, 0x02d8, 0x0bb9, 0x00d8, 0x08d8, 0x04d8, 0x0fb9,
0x1017, 0x0538, 0x0138, 0x11b8, 0x1117, 0x0738, 0x0338, 0x0c79,
0x1097, 0x0638, 0x0238, 0x0a79, 0x0038, 0x0838, 0x0438, 0x0e79,
0x1057, 0x05b8, 0x01b8, 0x0979, 0x1157, 0x07b8, 0x03b8, 0x0d79,
0x10d7, 0x06b8, 0x02b8, 0x0b79, 0x00b8, 0x08b8, 0x04b8, 0x0f79,
0x1037, 0x0578, 0x0178, 0x11f8, 0x1137, 0x0778, 0x0378, 0x0cf9,
0x10b7, 0x0678, 0x0278, 0x0af9, 0x0078, 0x0878, 0x0478, 0x0ef9,
0x1077, 0x05f8, 0x01f8, 0x09f9, 0x1177, 0x07f8, 0x03f8, 0x0df9,
0x10f7, 0x06f8, 0x02f8, 0x0bf9, 0x00f8, 0x08f8, 0x04f8, 0x0ff9,
},
nil, 0,
}

View File

@ -11,7 +11,9 @@ package flate
import (
"bytes"
"encoding/hex"
"io"
"io/ioutil"
"strings"
"testing"
)
@ -258,3 +260,15 @@ func TestStreams(t *testing.T) {
}
}
}
func TestTruncatedStreams(t *testing.T) {
const data = "\x00\f\x00\xf3\xffhello, world\x01\x00\x00\xff\xff"
for i := 0; i < len(data)-1; i++ {
r := NewReader(strings.NewReader(data[:i]))
_, err := io.Copy(ioutil.Discard, r)
if err != io.ErrUnexpectedEOF {
t.Errorf("io.Copy(%d) on truncated stream: got %v, want %v", i, err, io.ErrUnexpectedEOF)
}
}
}

View File

@ -1,265 +0,0 @@
// 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.
// +build ignore
// This program generates fixedhuff.go
// Invoke as
//
// go run gen.go -output fixedhuff.go
package main
import (
"bytes"
"flag"
"fmt"
"go/format"
"io/ioutil"
"log"
)
var filename = flag.String("output", "fixedhuff.go", "output file name")
const maxCodeLen = 16
// Note: the definition of the huffmanDecoder struct is copied from
// inflate.go, as it is private to the implementation.
// chunk & 15 is number of bits
// chunk >> 4 is value, including table link
const (
huffmanChunkBits = 9
huffmanNumChunks = 1 << huffmanChunkBits
huffmanCountMask = 15
huffmanValueShift = 4
)
type huffmanDecoder struct {
min int // the minimum code length
chunks [huffmanNumChunks]uint32 // chunks as described above
links [][]uint32 // overflow links
linkMask uint32 // mask the width of the link table
}
// Initialize Huffman decoding tables from array of code lengths.
// Following this function, h is guaranteed to be initialized into a complete
// tree (i.e., neither over-subscribed nor under-subscribed). The exception is a
// degenerate case where the tree has only a single symbol with length 1. Empty
// trees are permitted.
func (h *huffmanDecoder) init(bits []int) bool {
// Sanity enables additional runtime tests during Huffman
// table construction. It's intended to be used during
// development to supplement the currently ad-hoc unit tests.
const sanity = false
if h.min != 0 {
*h = huffmanDecoder{}
}
// Count number of codes of each length,
// compute min and max length.
var count [maxCodeLen]int
var min, max int
for _, n := range bits {
if n == 0 {
continue
}
if min == 0 || n < min {
min = n
}
if n > max {
max = n
}
count[n]++
}
// Empty tree. The decompressor.huffSym function will fail later if the tree
// is used. Technically, an empty tree is only valid for the HDIST tree and
// not the HCLEN and HLIT tree. However, a stream with an empty HCLEN tree
// is guaranteed to fail since it will attempt to use the tree to decode the
// codes for the HLIT and HDIST trees. Similarly, an empty HLIT tree is
// guaranteed to fail later since the compressed data section must be
// composed of at least one symbol (the end-of-block marker).
if max == 0 {
return true
}
code := 0
var nextcode [maxCodeLen]int
for i := min; i <= max; i++ {
code <<= 1
nextcode[i] = code
code += count[i]
}
// Check that the coding is complete (i.e., that we've
// assigned all 2-to-the-max possible bit sequences).
// Exception: To be compatible with zlib, we also need to
// accept degenerate single-code codings. See also
// TestDegenerateHuffmanCoding.
if code != 1<<uint(max) && !(code == 1 && max == 1) {
return false
}
h.min = min
if max > huffmanChunkBits {
numLinks := 1 << (uint(max) - huffmanChunkBits)
h.linkMask = uint32(numLinks - 1)
// create link tables
link := nextcode[huffmanChunkBits+1] >> 1
h.links = make([][]uint32, huffmanNumChunks-link)
for j := uint(link); j < huffmanNumChunks; j++ {
reverse := int(reverseByte[j>>8]) | int(reverseByte[j&0xff])<<8
reverse >>= uint(16 - huffmanChunkBits)
off := j - uint(link)
if sanity && h.chunks[reverse] != 0 {
panic("impossible: overwriting existing chunk")
}
h.chunks[reverse] = uint32(off<<huffmanValueShift | (huffmanChunkBits + 1))
h.links[off] = make([]uint32, numLinks)
}
}
for i, n := range bits {
if n == 0 {
continue
}
code := nextcode[n]
nextcode[n]++
chunk := uint32(i<<huffmanValueShift | n)
reverse := int(reverseByte[code>>8]) | int(reverseByte[code&0xff])<<8
reverse >>= uint(16 - n)
if n <= huffmanChunkBits {
for off := reverse; off < len(h.chunks); off += 1 << uint(n) {
// We should never need to overwrite
// an existing chunk. Also, 0 is
// never a valid chunk, because the
// lower 4 "count" bits should be
// between 1 and 15.
if sanity && h.chunks[off] != 0 {
panic("impossible: overwriting existing chunk")
}
h.chunks[off] = chunk
}
} else {
j := reverse & (huffmanNumChunks - 1)
if sanity && h.chunks[j]&huffmanCountMask != huffmanChunkBits+1 {
// Longer codes should have been
// associated with a link table above.
panic("impossible: not an indirect chunk")
}
value := h.chunks[j] >> huffmanValueShift
linktab := h.links[value]
reverse >>= huffmanChunkBits
for off := reverse; off < len(linktab); off += 1 << uint(n-huffmanChunkBits) {
if sanity && linktab[off] != 0 {
panic("impossible: overwriting existing chunk")
}
linktab[off] = chunk
}
}
}
if sanity {
// Above we've sanity checked that we never overwrote
// an existing entry. Here we additionally check that
// we filled the tables completely.
for i, chunk := range h.chunks {
if chunk == 0 {
// As an exception, in the degenerate
// single-code case, we allow odd
// chunks to be missing.
if code == 1 && i%2 == 1 {
continue
}
panic("impossible: missing chunk")
}
}
for _, linktab := range h.links {
for _, chunk := range linktab {
if chunk == 0 {
panic("impossible: missing chunk")
}
}
}
}
return true
}
func main() {
flag.Parse()
var h huffmanDecoder
var bits [288]int
initReverseByte()
for i := 0; i < 144; i++ {
bits[i] = 8
}
for i := 144; i < 256; i++ {
bits[i] = 9
}
for i := 256; i < 280; i++ {
bits[i] = 7
}
for i := 280; i < 288; i++ {
bits[i] = 8
}
h.init(bits[:])
if h.links != nil {
log.Fatal("Unexpected links table in fixed Huffman decoder")
}
var buf bytes.Buffer
fmt.Fprintf(&buf, `// 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.`+"\n\n")
fmt.Fprintln(&buf, "package flate")
fmt.Fprintln(&buf)
fmt.Fprintln(&buf, "// autogenerated by go run gen.go -output fixedhuff.go, DO NOT EDIT")
fmt.Fprintln(&buf)
fmt.Fprintln(&buf, "var fixedHuffmanDecoder = huffmanDecoder{")
fmt.Fprintf(&buf, "\t%d,\n", h.min)
fmt.Fprintln(&buf, "\t[huffmanNumChunks]uint32{")
for i := 0; i < huffmanNumChunks; i++ {
if i&7 == 0 {
fmt.Fprintf(&buf, "\t\t")
} else {
fmt.Fprintf(&buf, " ")
}
fmt.Fprintf(&buf, "0x%04x,", h.chunks[i])
if i&7 == 7 {
fmt.Fprintln(&buf)
}
}
fmt.Fprintln(&buf, "\t},")
fmt.Fprintln(&buf, "\tnil, 0,")
fmt.Fprintln(&buf, "}")
data, err := format.Source(buf.Bytes())
if err != nil {
log.Fatal(err)
}
err = ioutil.WriteFile(*filename, data, 0644)
if err != nil {
log.Fatal(err)
}
}
var reverseByte [256]byte
func initReverseByte() {
for x := 0; x < 256; x++ {
var result byte
for i := uint(0); i < 8; i++ {
result |= byte(((x >> i) & 1) << (7 - i))
}
reverseByte[x] = result
}
}

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:generate go run gen.go -output fixedhuff.go
// Package flate implements the DEFLATE compressed data format, described in
// RFC 1951. The gzip and zlib packages implement access to DEFLATE-based file
// formats.
@ -13,6 +11,7 @@ import (
"bufio"
"io"
"strconv"
"sync"
)
const (
@ -26,6 +25,10 @@ const (
numCodes = 19 // number of codes in Huffman meta-code
)
// Initialize the fixedHuffmanDecoder only once upon first use.
var fixedOnce sync.Once
var fixedHuffmanDecoder huffmanDecoder
// A CorruptInputError reports the presence of corrupt input at a given offset.
type CorruptInputError int64
@ -39,6 +42,8 @@ type InternalError string
func (e InternalError) Error() string { return "flate: internal error: " + string(e) }
// A ReadError reports an error encountered while reading input.
//
// Deprecated: No longer returned.
type ReadError struct {
Offset int64 // byte offset where error occurred
Err error // error returned by underlying Read
@ -49,6 +54,8 @@ func (e *ReadError) Error() string {
}
// A WriteError reports an error encountered while writing output.
//
// Deprecated: No longer returned.
type WriteError struct {
Offset int64 // byte offset where error occurred
Err error // error returned by underlying Write
@ -67,10 +74,6 @@ type Resetter interface {
Reset(r io.Reader, dict []byte) error
}
// Note that much of the implementation of huffmanDecoder is also copied
// into gen.go (in package main) for the purpose of precomputing the
// fixed huffman tables so they can be included statically.
// The data structure for decoding Huffman tables is based on that of
// zlib. There is a lookup table of a fixed bit width (huffmanChunkBits),
// For codes smaller than the table width, there are multiple entries
@ -78,12 +81,15 @@ type Resetter interface {
// larger than the table width, the table contains a link to an overflow
// table. The width of each entry in the link table is the maximum code
// size minus the chunk width.
//
// Note that you can do a lookup in the table even without all bits
// filled. Since the extra bits are zero, and the DEFLATE Huffman codes
// have the property that shorter codes come before longer ones, the
// bit length estimate in the result is a lower bound on the actual
// number of bits.
//
// See the following:
// http://www.gzip.org/algorithm.txt
// chunk & 15 is number of bits
// chunk >> 4 is value, including table link
@ -459,6 +465,14 @@ func (f *decompressor) readHuffman() error {
return CorruptInputError(f.roffset)
}
// As an optimization, we can initialize the min bits to read at a time
// for the HLIT tree to the length of the EOB marker since we know that
// every block must terminate with one. This preserves the property that
// we never read any extra bytes after the end of the DEFLATE stream.
if f.h1.min < f.bits[endBlockMarker] {
f.h1.min = f.bits[endBlockMarker]
}
return nil
}
@ -635,7 +649,10 @@ func (f *decompressor) dataBlock() {
nr, err := io.ReadFull(f.r, f.buf[0:4])
f.roffset += int64(nr)
if err != nil {
f.err = &ReadError{f.roffset, err}
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
f.err = err
return
}
n := int(f.buf[0]) | int(f.buf[1])<<8
@ -667,7 +684,10 @@ func (f *decompressor) copyData() {
m, err := io.ReadFull(f.r, f.hist[f.hp:f.hp+m])
f.roffset += int64(m)
if err != nil {
f.err = &ReadError{f.roffset, err}
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
f.err = err
return
}
n -= m
@ -760,6 +780,26 @@ func makeReader(r io.Reader) Reader {
return bufio.NewReader(r)
}
func fixedHuffmanDecoderInit() {
fixedOnce.Do(func() {
// These come from the RFC section 3.2.6.
var bits [288]int
for i := 0; i < 144; i++ {
bits[i] = 8
}
for i := 144; i < 256; i++ {
bits[i] = 9
}
for i := 256; i < 280; i++ {
bits[i] = 7
}
for i := 280; i < 288; i++ {
bits[i] = 8
}
fixedHuffmanDecoder.init(bits[:])
})
}
func (f *decompressor) Reset(r io.Reader, dict []byte) error {
*f = decompressor{
r: makeReader(r),
@ -783,11 +823,13 @@ func (f *decompressor) Reset(r io.Reader, dict []byte) error {
//
// The ReadCloser returned by NewReader also implements Resetter.
func NewReader(r io.Reader) io.ReadCloser {
fixedHuffmanDecoderInit()
var f decompressor
f.bits = new([maxNumLit + maxNumDist]int)
f.codebits = new([numCodes]int)
f.r = makeReader(r)
f.hist = new([maxHist]byte)
f.bits = new([maxNumLit + maxNumDist]int)
f.codebits = new([numCodes]int)
f.step = (*decompressor).nextBlock
return &f
}
@ -800,6 +842,8 @@ func NewReader(r io.Reader) io.ReadCloser {
//
// The ReadCloser returned by NewReader also implements Resetter.
func NewReaderDict(r io.Reader, dict []byte) io.ReadCloser {
fixedHuffmanDecoderInit()
var f decompressor
f.r = makeReader(r)
f.hist = new([maxHist]byte)

View File

@ -32,7 +32,7 @@ var testfiles = []string{
// does not repeat, but there are only 10 possible digits, so it should be
// reasonably compressible.
digits: "../testdata/e.txt",
// Twain is Project Gutenberg's edition of Mark Twain's classic English novel.
// Twain is Mark Twain's classic English novel.
twain: "../testdata/Mark.Twain-Tom.Sawyer.txt",
}

View File

@ -90,13 +90,11 @@ func lengthCode(len uint32) uint32 { return lengthCodes[len] }
// Returns the offset code corresponding to a specific offset
func offsetCode(off uint32) uint32 {
const n = uint32(len(offsetCodes))
switch {
case off < n:
if off < uint32(len(offsetCodes)) {
return offsetCodes[off]
case off>>7 < n:
return offsetCodes[off>>7] + 14
default:
return offsetCodes[off>>14] + 28
}
if off>>7 < uint32(len(offsetCodes)) {
return offsetCodes[off>>7] + 14
}
return offsetCodes[off>>14] + 28
}

View File

@ -43,6 +43,9 @@ var (
// The gzip file stores a header giving metadata about the compressed file.
// That header is exposed as the fields of the Writer and Reader structs.
//
// Strings must be UTF-8 encoded and may only contain Unicode code points
// U+0001 through U+00FF, due to limitations of the GZIP file format.
type Header struct {
Comment string // comment
Extra []byte // "extra data"
@ -66,7 +69,7 @@ type Header struct {
// returned by Read as tentative until they receive the io.EOF
// marking the end of the data.
type Reader struct {
Header
Header // valid after NewReader or Reader.Reset
r flate.Reader
decompressor io.ReadCloser
digest hash.Hash32
@ -80,7 +83,10 @@ type Reader struct {
// NewReader creates a new Reader reading the given reader.
// If r does not also implement io.ByteReader,
// the decompressor may read more data than necessary from r.
//
// It is the caller's responsibility to call Close on the Reader when done.
//
// The Reader.Header fields will be valid in the Reader returned.
func NewReader(r io.Reader) (*Reader, error) {
z := new(Reader)
z.r = makeReader(r)
@ -164,6 +170,9 @@ func (z *Reader) readString() (string, error) {
func (z *Reader) read2() (uint32, error) {
_, err := io.ReadFull(z.r, z.buf[0:2])
if err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return 0, err
}
return uint32(z.buf[0]) | uint32(z.buf[1])<<8, nil
@ -172,6 +181,13 @@ func (z *Reader) read2() (uint32, error) {
func (z *Reader) readHeader(save bool) error {
_, err := io.ReadFull(z.r, z.buf[0:10])
if err != nil {
// RFC1952 section 2.2 says the following:
// A gzip file consists of a series of "members" (compressed data sets).
//
// Other than this, the specification does not clarify whether a
// "series" is defined as "one or more" or "zero or more". To err on the
// side of caution, Go interprets this to mean "zero or more".
// Thus, it is okay to return io.EOF here.
return err
}
if z.buf[0] != gzipID1 || z.buf[1] != gzipID2 || z.buf[2] != gzipDeflate {
@ -193,6 +209,9 @@ func (z *Reader) readHeader(save bool) error {
}
data := make([]byte, n)
if _, err = io.ReadFull(z.r, data); err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return err
}
if save {
@ -257,6 +276,9 @@ func (z *Reader) Read(p []byte) (n int, err error) {
// Finished file; check checksum + size.
if _, err := io.ReadFull(z.r, z.buf[0:8]); err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
z.err = err
return 0, err
}

View File

@ -6,6 +6,7 @@ package gzip
import (
"bytes"
"compress/flate"
"io"
"io/ioutil"
"os"
@ -408,3 +409,34 @@ Found:
t.Fatalf("third reset: err=%v, want io.EOF", err)
}
}
func TestNilStream(t *testing.T) {
// Go liberally interprets RFC1952 section 2.2 to mean that a gzip file
// consist of zero or more members. Thus, we test that a nil stream is okay.
_, err := NewReader(bytes.NewReader(nil))
if err != io.EOF {
t.Fatalf("NewReader(nil) on empty stream: got %v, want io.EOF", err)
}
}
func TestTruncatedStreams(t *testing.T) {
const data = "\x1f\x8b\b\x04\x00\tn\x88\x00\xff\a\x00foo bar\xcbH\xcd\xc9\xc9\xd7Q(\xcf/\xcaI\x01\x04:r\xab\xff\f\x00\x00\x00"
// Intentionally iterate starting with at least one byte in the stream.
for i := 1; i < len(data)-1; i++ {
r, err := NewReader(strings.NewReader(data[:i]))
if err != nil {
if err != io.ErrUnexpectedEOF {
t.Errorf("NewReader(%d) on truncated stream: got %v, want %v", i, err, io.ErrUnexpectedEOF)
}
continue
}
_, err = io.Copy(ioutil.Discard, r)
if ferr, ok := err.(*flate.ReadError); ok {
err = ferr.Err
}
if err != io.ErrUnexpectedEOF {
t.Errorf("io.Copy(%d) on truncated stream: got %v, want %v", i, err, io.ErrUnexpectedEOF)
}
}
}

View File

@ -25,7 +25,7 @@ const (
// A Writer is an io.WriteCloser.
// Writes to a Writer are compressed and written to w.
type Writer struct {
Header
Header // written at first call to Write, Flush, or Close
w io.Writer
level int
wroteHeader bool
@ -44,10 +44,7 @@ type Writer struct {
// Writes may be buffered and not flushed until Close.
//
// Callers that wish to set the fields in Writer.Header must do so before
// the first call to Write or Close. The Comment and Name header fields are
// UTF-8 strings in Go, but the underlying format requires NUL-terminated ISO
// 8859-1 (Latin-1). NUL or non-Latin-1 runes in those strings will lead to an
// error on Write.
// the first call to Write, Flush, or Close.
func NewWriter(w io.Writer) *Writer {
z, _ := NewWriterLevel(w, DefaultCompression)
return z

View File

@ -132,6 +132,7 @@ func (d *decoder) Read(b []byte) (int, error) {
// litWidth is the width in bits of literal codes.
func (d *decoder) decode() {
// Loop over the code stream, converting codes into decompressed bytes.
loop:
for {
code, err := d.read(d)
if err != nil {
@ -139,8 +140,7 @@ func (d *decoder) decode() {
err = io.ErrUnexpectedEOF
}
d.err = err
d.flush()
return
break
}
switch {
case code < d.clear:
@ -159,9 +159,8 @@ func (d *decoder) decode() {
d.last = decoderInvalidCode
continue
case code == d.eof:
d.flush()
d.err = io.EOF
return
break loop
case code <= d.hi:
c, i := code, len(d.output)-1
if code == d.hi {
@ -191,8 +190,7 @@ func (d *decoder) decode() {
}
default:
d.err = errors.New("lzw: invalid code")
d.flush()
return
break loop
}
d.last, d.hi = code, d.hi+1
if d.hi >= d.overflow {
@ -204,13 +202,10 @@ func (d *decoder) decode() {
}
}
if d.o >= flushBuffer {
d.flush()
return
break
}
}
}
func (d *decoder) flush() {
// Flush pending output.
d.toRead = d.output[:d.o]
d.o = 0
}

View File

@ -5,6 +5,7 @@
package lzw
import (
"internal/testenv"
"io"
"io/ioutil"
"os"
@ -13,6 +14,7 @@ import (
)
var filenames = []string{
"../testdata/gettysburg.txt",
"../testdata/e.txt",
"../testdata/pi.txt",
}
@ -89,10 +91,16 @@ func TestWriter(t *testing.T) {
for _, filename := range filenames {
for _, order := range [...]Order{LSB, MSB} {
// The test data "2.71828 etcetera" is ASCII text requiring at least 6 bits.
for _, litWidth := range [...]int{6, 7, 8} {
for litWidth := 6; litWidth <= 8; litWidth++ {
if filename == "../testdata/gettysburg.txt" && litWidth == 6 {
continue
}
testFile(t, filename, order, litWidth)
}
}
if testing.Short() && testenv.Builder() == "" {
break
}
}
}

View File

@ -1,27 +1,3 @@
The Project Gutenberg EBook of The Adventures of Tom Sawyer, Complete
by Mark Twain (Samuel Clemens)
This eBook is for the use of anyone anywhere at no cost and with
almost no restrictions whatsoever. You may copy it, give it away or
re-use it under the terms of the Project Gutenberg License included
with this eBook or online at www.gutenberg.net
Title: The Adventures of Tom Sawyer, Complete
Author: Mark Twain (Samuel Clemens)
Release Date: August 20, 2006 [EBook #74]
[Last updated: May 3, 2011]
Language: English
*** START OF THIS PROJECT GUTENBERG EBOOK TOM SAWYER ***
Produced by David Widger. The previous edition was updated by Jose
Menendez.
@ -8487,372 +8463,3 @@ prosperous and happy. Some day it may seem worth while to take up the
story of the younger ones again and see what sort of men and women they
turned out to be; therefore it will be wisest not to reveal any of that
part of their lives at present.
End of the Project Gutenberg EBook of The Adventures of Tom Sawyer, Complete
by Mark Twain (Samuel Clemens)
*** END OF THIS PROJECT GUTENBERG EBOOK TOM SAWYER ***
***** This file should be named 74.txt or 74.zip *****
This and all associated files of various formats will be found in:
http://www.gutenberg.net/7/74/
Produced by David Widger. The previous edition was update by Jose
Menendez.
Updated editions will replace the previous one--the old editions
will be renamed.
Creating the works from public domain print editions means that no
one owns a United States copyright in these works, so the Foundation
(and you!) can copy and distribute it in the United States without
permission and without paying copyright royalties. Special rules,
set forth in the General Terms of Use part of this license, apply to
copying and distributing Project Gutenberg-tm electronic works to
protect the PROJECT GUTENBERG-tm concept and trademark. Project
Gutenberg is a registered trademark, and may not be used if you
charge for the eBooks, unless you receive specific permission. If you
do not charge anything for copies of this eBook, complying with the
rules is very easy. You may use this eBook for nearly any purpose
such as creation of derivative works, reports, performances and
research. They may be modified and printed and given away--you may do
practically ANYTHING with public domain eBooks. Redistribution is
subject to the trademark license, especially commercial
redistribution.
*** START: FULL LICENSE ***
THE FULL PROJECT GUTENBERG LICENSE
PLEASE READ THIS BEFORE YOU DISTRIBUTE OR USE THIS WORK
To protect the Project Gutenberg-tm mission of promoting the free
distribution of electronic works, by using or distributing this work
(or any other work associated in any way with the phrase "Project
Gutenberg"), you agree to comply with all the terms of the Full Project
Gutenberg-tm License (available with this file or online at
http://gutenberg.net/license).
Section 1. General Terms of Use and Redistributing Project Gutenberg-tm
electronic works
1.A. By reading or using any part of this Project Gutenberg-tm
electronic work, you indicate that you have read, understand, agree to
and accept all the terms of this license and intellectual property
(trademark/copyright) agreement. If you do not agree to abide by all
the terms of this agreement, you must cease using and return or destroy
all copies of Project Gutenberg-tm electronic works in your possession.
If you paid a fee for obtaining a copy of or access to a Project
Gutenberg-tm electronic work and you do not agree to be bound by the
terms of this agreement, you may obtain a refund from the person or
entity to whom you paid the fee as set forth in paragraph 1.E.8.
1.B. "Project Gutenberg" is a registered trademark. It may only be
used on or associated in any way with an electronic work by people who
agree to be bound by the terms of this agreement. There are a few
things that you can do with most Project Gutenberg-tm electronic works
even without complying with the full terms of this agreement. See
paragraph 1.C below. There are a lot of things you can do with Project
Gutenberg-tm electronic works if you follow the terms of this agreement
and help preserve free future access to Project Gutenberg-tm electronic
works. See paragraph 1.E below.
1.C. The Project Gutenberg Literary Archive Foundation ("the Foundation"
or PGLAF), owns a compilation copyright in the collection of Project
Gutenberg-tm electronic works. Nearly all the individual works in the
collection are in the public domain in the United States. If an
individual work is in the public domain in the United States and you are
located in the United States, we do not claim a right to prevent you from
copying, distributing, performing, displaying or creating derivative
works based on the work as long as all references to Project Gutenberg
are removed. Of course, we hope that you will support the Project
Gutenberg-tm mission of promoting free access to electronic works by
freely sharing Project Gutenberg-tm works in compliance with the terms of
this agreement for keeping the Project Gutenberg-tm name associated with
the work. You can easily comply with the terms of this agreement by
keeping this work in the same format with its attached full Project
Gutenberg-tm License when you share it without charge with others.
1.D. The copyright laws of the place where you are located also govern
what you can do with this work. Copyright laws in most countries are in
a constant state of change. If you are outside the United States, check
the laws of your country in addition to the terms of this agreement
before downloading, copying, displaying, performing, distributing or
creating derivative works based on this work or any other Project
Gutenberg-tm work. The Foundation makes no representations concerning
the copyright status of any work in any country outside the United
States.
1.E. Unless you have removed all references to Project Gutenberg:
1.E.1. The following sentence, with active links to, or other immediate
access to, the full Project Gutenberg-tm License must appear prominently
whenever any copy of a Project Gutenberg-tm work (any work on which the
phrase "Project Gutenberg" appears, or with which the phrase "Project
Gutenberg" is associated) is accessed, displayed, performed, viewed,
copied or distributed:
This eBook is for the use of anyone anywhere at no cost and with
almost no restrictions whatsoever. You may copy it, give it away or
re-use it under the terms of the Project Gutenberg License included
with this eBook or online at www.gutenberg.net
1.E.2. If an individual Project Gutenberg-tm electronic work is derived
from the public domain (does not contain a notice indicating that it is
posted with permission of the copyright holder), the work can be copied
and distributed to anyone in the United States without paying any fees
or charges. If you are redistributing or providing access to a work
with the phrase "Project Gutenberg" associated with or appearing on the
work, you must comply either with the requirements of paragraphs 1.E.1
through 1.E.7 or obtain permission for the use of the work and the
Project Gutenberg-tm trademark as set forth in paragraphs 1.E.8 or
1.E.9.
1.E.3. If an individual Project Gutenberg-tm electronic work is posted
with the permission of the copyright holder, your use and distribution
must comply with both paragraphs 1.E.1 through 1.E.7 and any additional
terms imposed by the copyright holder. Additional terms will be linked
to the Project Gutenberg-tm License for all works posted with the
permission of the copyright holder found at the beginning of this work.
1.E.4. Do not unlink or detach or remove the full Project Gutenberg-tm
License terms from this work, or any files containing a part of this
work or any other work associated with Project Gutenberg-tm.
1.E.5. Do not copy, display, perform, distribute or redistribute this
electronic work, or any part of this electronic work, without
prominently displaying the sentence set forth in paragraph 1.E.1 with
active links or immediate access to the full terms of the Project
Gutenberg-tm License.
1.E.6. You may convert to and distribute this work in any binary,
compressed, marked up, nonproprietary or proprietary form, including any
word processing or hypertext form. However, if you provide access to or
distribute copies of a Project Gutenberg-tm work in a format other than
"Plain Vanilla ASCII" or other format used in the official version
posted on the official Project Gutenberg-tm web site (www.gutenberg.net),
you must, at no additional cost, fee or expense to the user, provide a
copy, a means of exporting a copy, or a means of obtaining a copy upon
request, of the work in its original "Plain Vanilla ASCII" or other
form. Any alternate format must include the full Project Gutenberg-tm
License as specified in paragraph 1.E.1.
1.E.7. Do not charge a fee for access to, viewing, displaying,
performing, copying or distributing any Project Gutenberg-tm works
unless you comply with paragraph 1.E.8 or 1.E.9.
1.E.8. You may charge a reasonable fee for copies of or providing
access to or distributing Project Gutenberg-tm electronic works provided
that
- You pay a royalty fee of 20% of the gross profits you derive from
the use of Project Gutenberg-tm works calculated using the method
you already use to calculate your applicable taxes. The fee is
owed to the owner of the Project Gutenberg-tm trademark, but he
has agreed to donate royalties under this paragraph to the
Project Gutenberg Literary Archive Foundation. Royalty payments
must be paid within 60 days following each date on which you
prepare (or are legally required to prepare) your periodic tax
returns. Royalty payments should be clearly marked as such and
sent to the Project Gutenberg Literary Archive Foundation at the
address specified in Section 4, "Information about donations to
the Project Gutenberg Literary Archive Foundation."
- You provide a full refund of any money paid by a user who notifies
you in writing (or by e-mail) within 30 days of receipt that s/he
does not agree to the terms of the full Project Gutenberg-tm
License. You must require such a user to return or
destroy all copies of the works possessed in a physical medium
and discontinue all use of and all access to other copies of
Project Gutenberg-tm works.
- You provide, in accordance with paragraph 1.F.3, a full refund of any
money paid for a work or a replacement copy, if a defect in the
electronic work is discovered and reported to you within 90 days
of receipt of the work.
- You comply with all other terms of this agreement for free
distribution of Project Gutenberg-tm works.
1.E.9. If you wish to charge a fee or distribute a Project Gutenberg-tm
electronic work or group of works on different terms than are set
forth in this agreement, you must obtain permission in writing from
both the Project Gutenberg Literary Archive Foundation and Michael
Hart, the owner of the Project Gutenberg-tm trademark. Contact the
Foundation as set forth in Section 3 below.
1.F.
1.F.1. Project Gutenberg volunteers and employees expend considerable
effort to identify, do copyright research on, transcribe and proofread
public domain works in creating the Project Gutenberg-tm
collection. Despite these efforts, Project Gutenberg-tm electronic
works, and the medium on which they may be stored, may contain
"Defects," such as, but not limited to, incomplete, inaccurate or
corrupt data, transcription errors, a copyright or other intellectual
property infringement, a defective or damaged disk or other medium, a
computer virus, or computer codes that damage or cannot be read by
your equipment.
1.F.2. LIMITED WARRANTY, DISCLAIMER OF DAMAGES - Except for the "Right
of Replacement or Refund" described in paragraph 1.F.3, the Project
Gutenberg Literary Archive Foundation, the owner of the Project
Gutenberg-tm trademark, and any other party distributing a Project
Gutenberg-tm electronic work under this agreement, disclaim all
liability to you for damages, costs and expenses, including legal
fees. YOU AGREE THAT YOU HAVE NO REMEDIES FOR NEGLIGENCE, STRICT
LIABILITY, BREACH OF WARRANTY OR BREACH OF CONTRACT EXCEPT THOSE
PROVIDED IN PARAGRAPH F3. YOU AGREE THAT THE FOUNDATION, THE
TRADEMARK OWNER, AND ANY DISTRIBUTOR UNDER THIS AGREEMENT WILL NOT BE
LIABLE TO YOU FOR ACTUAL, DIRECT, INDIRECT, CONSEQUENTIAL, PUNITIVE OR
INCIDENTAL DAMAGES EVEN IF YOU GIVE NOTICE OF THE POSSIBILITY OF SUCH
DAMAGE.
1.F.3. LIMITED RIGHT OF REPLACEMENT OR REFUND - If you discover a
defect in this electronic work within 90 days of receiving it, you can
receive a refund of the money (if any) you paid for it by sending a
written explanation to the person you received the work from. If you
received the work on a physical medium, you must return the medium with
your written explanation. The person or entity that provided you with
the defective work may elect to provide a replacement copy in lieu of a
refund. If you received the work electronically, the person or entity
providing it to you may choose to give you a second opportunity to
receive the work electronically in lieu of a refund. If the second copy
is also defective, you may demand a refund in writing without further
opportunities to fix the problem.
1.F.4. Except for the limited right of replacement or refund set forth
in paragraph 1.F.3, this work is provided to you 'AS-IS' WITH NO OTHER
WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
WARRANTIES OF MERCHANTIBILITY OR FITNESS FOR ANY PURPOSE.
1.F.5. Some states do not allow disclaimers of certain implied
warranties or the exclusion or limitation of certain types of damages.
If any disclaimer or limitation set forth in this agreement violates the
law of the state applicable to this agreement, the agreement shall be
interpreted to make the maximum disclaimer or limitation permitted by
the applicable state law. The invalidity or unenforceability of any
provision of this agreement shall not void the remaining provisions.
1.F.6. INDEMNITY - You agree to indemnify and hold the Foundation, the
trademark owner, any agent or employee of the Foundation, anyone
providing copies of Project Gutenberg-tm electronic works in accordance
with this agreement, and any volunteers associated with the production,
promotion and distribution of Project Gutenberg-tm electronic works,
harmless from all liability, costs and expenses, including legal fees,
that arise directly or indirectly from any of the following which you do
or cause to occur: (a) distribution of this or any Project Gutenberg-tm
work, (b) alteration, modification, or additions or deletions to any
Project Gutenberg-tm work, and (c) any Defect you cause.
Section 2. Information about the Mission of Project Gutenberg-tm
Project Gutenberg-tm is synonymous with the free distribution of
electronic works in formats readable by the widest variety of computers
including obsolete, old, middle-aged and new computers. It exists
because of the efforts of hundreds of volunteers and donations from
people in all walks of life.
Volunteers and financial support to provide volunteers with the
assistance they need, is critical to reaching Project Gutenberg-tm's
goals and ensuring that the Project Gutenberg-tm collection will
remain freely available for generations to come. In 2001, the Project
Gutenberg Literary Archive Foundation was created to provide a secure
and permanent future for Project Gutenberg-tm and future generations.
To learn more about the Project Gutenberg Literary Archive Foundation
and how your efforts and donations can help, see Sections 3 and 4
and the Foundation web page at http://www.pglaf.org.
Section 3. Information about the Project Gutenberg Literary Archive
Foundation
The Project Gutenberg Literary Archive Foundation is a non profit
501(c)(3) educational corporation organized under the laws of the
state of Mississippi and granted tax exempt status by the Internal
Revenue Service. The Foundation's EIN or federal tax identification
number is 64-6221541. Its 501(c)(3) letter is posted at
http://pglaf.org/fundraising. Contributions to the Project Gutenberg
Literary Archive Foundation are tax deductible to the full extent
permitted by U.S. federal laws and your state's laws.
The Foundation's principal office is located at 4557 Melan Dr. S.
Fairbanks, AK, 99712., but its volunteers and employees are scattered
throughout numerous locations. Its business office is located at
809 North 1500 West, Salt Lake City, UT 84116, (801) 596-1887, email
business@pglaf.org. Email contact links and up to date contact
information can be found at the Foundation's web site and official
page at http://pglaf.org
For additional contact information:
Dr. Gregory B. Newby
Chief Executive and Director
gbnewby@pglaf.org
Section 4. Information about Donations to the Project Gutenberg
Literary Archive Foundation
Project Gutenberg-tm depends upon and cannot survive without wide
spread public support and donations to carry out its mission of
increasing the number of public domain and licensed works that can be
freely distributed in machine readable form accessible by the widest
array of equipment including outdated equipment. Many small donations
($1 to $5,000) are particularly important to maintaining tax exempt
status with the IRS.
The Foundation is committed to complying with the laws regulating
charities and charitable donations in all 50 states of the United
States. Compliance requirements are not uniform and it takes a
considerable effort, much paperwork and many fees to meet and keep up
with these requirements. We do not solicit donations in locations
where we have not received written confirmation of compliance. To
SEND DONATIONS or determine the status of compliance for any
particular state visit http://pglaf.org
While we cannot and do not solicit contributions from states where we
have not met the solicitation requirements, we know of no prohibition
against accepting unsolicited donations from donors in such states who
approach us with offers to donate.
International donations are gratefully accepted, but we cannot make
any statements concerning tax treatment of donations received from
outside the United States. U.S. laws alone swamp our small staff.
Please check the Project Gutenberg Web pages for current donation
methods and addresses. Donations are accepted in a number of other
ways including including checks, online payments and credit card
donations. To donate, please visit: http://pglaf.org/donate
Section 5. General Information About Project Gutenberg-tm electronic
works.
Professor Michael S. Hart is the originator of the Project Gutenberg-tm
concept of a library of electronic works that could be freely shared
with anyone. For thirty years, he produced and distributed Project
Gutenberg-tm eBooks with only a loose network of volunteer support.
Project Gutenberg-tm eBooks are often created from several printed
editions, all of which are confirmed as Public Domain in the U.S.
unless a copyright notice is included. Thus, we do not necessarily
keep eBooks in compliance with any particular paper edition.
Most people start at our Web site which has the main PG search facility:
http://www.gutenberg.net
This Web site includes information about Project Gutenberg-tm,
including how to make donations to the Project Gutenberg Literary
Archive Foundation, how to help produce our new eBooks, and how to
subscribe to our email newsletter to hear about new eBooks.

View File

@ -0,0 +1,29 @@
Four score and seven years ago our fathers brought forth on
this continent, a new nation, conceived in Liberty, and dedicated
to the proposition that all men are created equal.
Now we are engaged in a great Civil War, testing whether that
nation, or any nation so conceived and so dedicated, can long
endure.
We are met on a great battle-field of that war.
We have come to dedicate a portion of that field, as a final
resting place for those who here gave their lives that that
nation might live. It is altogether fitting and proper that
we should do this.
But, in a larger sense, we can not dedicate - we can not
consecrate - we can not hallow - this ground.
The brave men, living and dead, who struggled here, have
consecrated it, far above our poor power to add or detract.
The world will little note, nor long remember what we say here,
but it can never forget what they did here.
It is for us the living, rather, to be dedicated here to the
unfinished work which they who fought here have thus far so
nobly advanced. It is rather for us to be here dedicated to
the great task remaining before us - that from these honored
dead we take increased devotion to that cause for which they
gave the last full measure of devotion -
that we here highly resolve that these dead shall not have
died in vain - that this nation, under God, shall have a new
birth of freedom - and that government of the people, by the
people, for the people, shall not perish from this earth.
Abraham Lincoln, November 19, 1863, Gettysburg, Pennsylvania

View File

@ -101,6 +101,9 @@ func (z *reader) Read(p []byte) (n int, err error) {
// Finished file; check checksum.
if _, err := io.ReadFull(z.r, z.scratch[0:4]); err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
z.err = err
return 0, err
}
@ -130,6 +133,9 @@ func (z *reader) Reset(r io.Reader, dict []byte) error {
}
_, err := io.ReadFull(z.r, z.scratch[0:2])
if err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return err
}
h := uint(z.scratch[0])<<8 | uint(z.scratch[1])
@ -140,6 +146,9 @@ func (z *reader) Reset(r io.Reader, dict []byte) error {
if haveDict {
_, err = io.ReadFull(z.r, z.scratch[0:4])
if err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return err
}
checksum := uint32(z.scratch[0])<<24 | uint32(z.scratch[1])<<16 | uint32(z.scratch[2])<<8 | uint32(z.scratch[3])

View File

@ -22,6 +22,30 @@ type zlibTest struct {
// http://www.zlib.net/zpipe.c
var zlibTests = []zlibTest{
{
"truncated empty",
"",
[]byte{},
nil,
io.ErrUnexpectedEOF,
},
{
"truncated dict",
"",
[]byte{0x78, 0xbb},
[]byte{0x00},
io.ErrUnexpectedEOF,
},
{
"truncated checksum",
"",
[]byte{0x78, 0xbb, 0x00, 0x01, 0x00, 0x01, 0xca, 0x48,
0xcd, 0xc9, 0xc9, 0xd7, 0x51, 0x28, 0xcf, 0x2f,
0xca, 0x49, 0x01, 0x04, 0x00, 0x00, 0xff, 0xff,
},
[]byte{0x00},
io.ErrUnexpectedEOF,
},
{
"empty",
"",

View File

@ -7,6 +7,7 @@ package zlib
import (
"bytes"
"fmt"
"internal/testenv"
"io"
"io/ioutil"
"os"
@ -14,6 +15,7 @@ import (
)
var filenames = []string{
"../testdata/gettysburg.txt",
"../testdata/e.txt",
"../testdata/pi.txt",
}
@ -152,22 +154,34 @@ func TestWriter(t *testing.T) {
}
func TestWriterBig(t *testing.T) {
for _, fn := range filenames {
for i, fn := range filenames {
testFileLevelDict(t, fn, DefaultCompression, "")
testFileLevelDict(t, fn, NoCompression, "")
for level := BestSpeed; level <= BestCompression; level++ {
testFileLevelDict(t, fn, level, "")
if level >= 1 && testing.Short() && testenv.Builder() == "" {
break
}
}
if i == 0 && testing.Short() && testenv.Builder() == "" {
break
}
}
}
func TestWriterDict(t *testing.T) {
const dictionary = "0123456789."
for _, fn := range filenames {
for i, fn := range filenames {
testFileLevelDict(t, fn, DefaultCompression, dictionary)
testFileLevelDict(t, fn, NoCompression, dictionary)
for level := BestSpeed; level <= BestCompression; level++ {
testFileLevelDict(t, fn, level, dictionary)
if level >= 1 && testing.Short() && testenv.Builder() == "" {
break
}
}
if i == 0 && testing.Short() && testenv.Builder() == "" {
break
}
}
}
@ -179,10 +193,11 @@ func TestWriterReset(t *testing.T) {
testFileLevelDictReset(t, fn, DefaultCompression, nil)
testFileLevelDictReset(t, fn, NoCompression, []byte(dictionary))
testFileLevelDictReset(t, fn, DefaultCompression, []byte(dictionary))
if !testing.Short() {
for level := BestSpeed; level <= BestCompression; level++ {
testFileLevelDictReset(t, fn, level, nil)
}
if testing.Short() {
break
}
for level := BestSpeed; level <= BestCompression; level++ {
testFileLevelDictReset(t, fn, level, nil)
}
}
}

View File

@ -0,0 +1,172 @@
// 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
package aes
import (
"crypto/cipher"
"crypto/subtle"
"errors"
)
// The following functions are defined in gcm_amd64.s.
func hasGCMAsm() bool
//go:noescape
func aesEncBlock(dst, src *[16]byte, ks []uint32)
//go:noescape
func gcmAesInit(productTable *[256]byte, ks []uint32)
//go:noescape
func gcmAesData(productTable *[256]byte, data []byte, T *[16]byte)
//go:noescape
func gcmAesEnc(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, ks []uint32)
//go:noescape
func gcmAesDec(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, ks []uint32)
//go:noescape
func gcmAesFinish(productTable *[256]byte, tagMask, T *[16]byte, pLen, dLen uint64)
const (
gcmBlockSize = 16
gcmTagSize = 16
gcmStandardNonceSize = 12
)
var errOpen = errors.New("cipher: message authentication failed")
// aesCipherGCM implements crypto/cipher.gcmAble so that crypto/cipher.NewGCM
// will use the optimised implementation in this file when possible. Instances
// of this type only exist when hasGCMAsm returns true.
type aesCipherGCM struct {
aesCipher
}
// NewGCM returns the AES cipher wrapped in Galois Counter Mode. This is only
// called by crypto/cipher.NewGCM via the gcmAble interface.
func (c *aesCipherGCM) NewGCM(nonceSize int) (cipher.AEAD, error) {
g := &gcmAsm{ks: c.enc, nonceSize: nonceSize}
gcmAesInit(&g.productTable, g.ks)
return g, nil
}
type gcmAsm struct {
// ks is the key schedule, the length of which depends on the size of
// the AES key.
ks []uint32
// productTable contains pre-computed multiples of the binary-field
// element used in GHASH.
productTable [256]byte
// nonceSize contains the expected size of the nonce, in bytes.
nonceSize int
}
func (g *gcmAsm) NonceSize() int {
return g.nonceSize
}
func (*gcmAsm) Overhead() int {
return gcmTagSize
}
// sliceForAppend takes a slice and a requested number of bytes. It returns a
// slice with the contents of the given slice followed by that many bytes and a
// second slice that aliases into it and contains only the extra bytes. If the
// original slice has sufficient capacity then no allocation is performed.
func sliceForAppend(in []byte, n int) (head, tail []byte) {
if total := len(in) + n; cap(in) >= total {
head = in[:total]
} else {
head = make([]byte, total)
copy(head, in)
}
tail = head[len(in):]
return
}
// Seal encrypts and authenticates plaintext. See the cipher.AEAD interface for
// details.
func (g *gcmAsm) Seal(dst, nonce, plaintext, data []byte) []byte {
if len(nonce) != g.nonceSize {
panic("cipher: incorrect nonce length given to GCM")
}
var counter, tagMask [gcmBlockSize]byte
if len(nonce) == gcmStandardNonceSize {
// Init counter to nonce||1
copy(counter[:], nonce)
counter[gcmBlockSize-1] = 1
} else {
// Otherwise counter = GHASH(nonce)
gcmAesData(&g.productTable, nonce, &counter)
gcmAesFinish(&g.productTable, &tagMask, &counter, uint64(len(nonce)), uint64(0))
}
aesEncBlock(&tagMask, &counter, g.ks)
var tagOut [gcmTagSize]byte
gcmAesData(&g.productTable, data, &tagOut)
ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize)
if len(plaintext) > 0 {
gcmAesEnc(&g.productTable, out, plaintext, &counter, &tagOut, g.ks)
}
gcmAesFinish(&g.productTable, &tagMask, &tagOut, uint64(len(plaintext)), uint64(len(data)))
copy(out[len(plaintext):], tagOut[:])
return ret
}
// Open authenticates and decrypts ciphertext. See the cipher.AEAD interface
// for details.
func (g *gcmAsm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
if len(nonce) != g.nonceSize {
panic("cipher: incorrect nonce length given to GCM")
}
if len(ciphertext) < gcmTagSize {
return nil, errOpen
}
tag := ciphertext[len(ciphertext)-gcmTagSize:]
ciphertext = ciphertext[:len(ciphertext)-gcmTagSize]
// See GCM spec, section 7.1.
var counter, tagMask [gcmBlockSize]byte
if len(nonce) == gcmStandardNonceSize {
// Init counter to nonce||1
copy(counter[:], nonce)
counter[gcmBlockSize-1] = 1
} else {
// Otherwise counter = GHASH(nonce)
gcmAesData(&g.productTable, nonce, &counter)
gcmAesFinish(&g.productTable, &tagMask, &counter, uint64(len(nonce)), uint64(0))
}
aesEncBlock(&tagMask, &counter, g.ks)
var expectedTag [gcmTagSize]byte
gcmAesData(&g.productTable, data, &expectedTag)
ret, out := sliceForAppend(dst, len(ciphertext))
if len(ciphertext) > 0 {
gcmAesDec(&g.productTable, out, ciphertext, &counter, &expectedTag, g.ks)
}
gcmAesFinish(&g.productTable, &tagMask, &expectedTag, uint64(len(ciphertext)), uint64(len(data)))
if subtle.ConstantTimeCompare(expectedTag[:], tag) != 1 {
for i := range out {
out[i] = 0
}
return nil, errOpen
}
return ret, nil
}

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