[dev.boringcrypto] all: merge master into dev.boringcrypto

Change-Id: Ic1d89215bb3e37a722d3d3bc7698edea940a83d9
This commit is contained in:
Filippo Valsorda 2019-06-27 18:48:02 -04:00
commit 98188f3001
662 changed files with 9800 additions and 227044 deletions

13
SECURITY.md Normal file
View file

@ -0,0 +1,13 @@
# Security Policy
## Supported Versions
We support the past two Go releases (for example, Go 1.11.x and Go 1.12.x).
See https://golang.org/wiki/Go-Release-Cycle and in particular the
[Release Maintenance](https://github.com/golang/go/wiki/Go-Release-Cycle#release-maintenance)
part of that page.
## Reporting a Vulnerability
See https://golang.org/security for how to report a vulnerability.

View file

@ -26,7 +26,7 @@ see <a href="gccgo_contribute.html">Contributing to gccgo</a>.
<h2 id="contributor">Becoming a contributor</h2> <h2 id="contributor">Becoming a contributor</h2>
<h3>Overview</h3> <h3 id="contrib_overview">Overview</h3>
<p> <p>
The first step is registering as a Go contributor and configuring your environment. The first step is registering as a Go contributor and configuring your environment.
@ -261,7 +261,7 @@ a new issue</a> or by claiming
an <a href="https://golang.org/issues">existing one</a>. an <a href="https://golang.org/issues">existing one</a>.
</p> </p>
<h3>Check the issue tracker</h3> <h3 id="check_tracker">Check the issue tracker</h3>
<p> <p>
Whether you already know what contribution to make, or you are searching for Whether you already know what contribution to make, or you are searching for
@ -398,7 +398,7 @@ It's different but powerful and familiarity with it will help you understand
the flow. the flow.
</p> </p>
<h3>Overview</h3> <h3 id="gerrit_overview">Overview</h3>
<p> <p>
This is an overview of the overall process: This is an overview of the overall process:
@ -666,7 +666,7 @@ The algorithm is described at https://wikipedia.org/wiki/McGillicutty_Algorithm
Fixes #159 Fixes #159
</pre> </pre>
<h3>First line</h3> <h3 id="first_line">First line</h3>
<p> <p>
The first line of the change description is conventionally a short one-line The first line of the change description is conventionally a short one-line
@ -684,7 +684,7 @@ and actually summarizes the result of the change.
Follow the first line by a blank line. Follow the first line by a blank line.
</p> </p>
<h3>Main content</h3> <h3 id="main_content">Main content</h3>
<p> <p>
The rest of the description elaborates and should provide context for the The rest of the description elaborates and should provide context for the
@ -702,7 +702,7 @@ tool is conventionally used to format
benchmark data for change descriptions. benchmark data for change descriptions.
</p> </p>
<h3>Referencing issues</h3> <h3 id="ref_issues">Referencing issues</h3>
<p> <p>
The special notation "Fixes #12345" associates the change with issue 12345 in the The special notation "Fixes #12345" associates the change with issue 12345 in the

View file

@ -69,6 +69,14 @@ the go command, the runtime, and the <code>os</code> package. See the
1.12.5 milestone</a> on our issue tracker for details. 1.12.5 milestone</a> on our issue tracker for details.
</p> </p>
<p>
go1.12.6 (released 2019/06/11) includes fixes to the compiler, the linker,
the go command, and the <code>crypto/x509</code>, <code>net/http</code>, and
<code>os</code> packages. See the
<a href="https://github.com/golang/go/issues?q=milestone%3AGo1.12.6">Go
1.12.6 milestone</a> on our issue tracker for details.
</p>
<h2 id="go1.11">go1.11 (released 2018/08/24)</h2> <h2 id="go1.11">go1.11 (released 2018/08/24)</h2>
<p> <p>
@ -154,6 +162,12 @@ See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.11.10">Go
1.11.10 milestone</a> on our issue tracker for details. 1.11.10 milestone</a> on our issue tracker for details.
</p> </p>
<p>
go1.11.11 (released 2019/06/11) includes a fix to the <code>crypto/x509</code> package.
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.11.11">Go
1.11.11 milestone</a> on our issue tracker for details.
</p>
<h2 id="go1.10">go1.10 (released 2018/02/16)</h2> <h2 id="go1.10">go1.10 (released 2018/02/16)</h2>
<p> <p>

View file

@ -24,6 +24,16 @@ Do not send CLs removing the interior tags from such phrases.
</strong> </strong>
</p> </p>
<p>
As of Go 1.13, the go command by default downloads and authenticates
modules using the Go module mirror and Go checksum database run by Google. See
<a href="https://proxy.golang.org/privacy">https://proxy.golang.org/privacy</a>
for privacy information about these services and the
<a href="/cmd/go/#hdr-Module_downloading_and_verification">go command documentation</a>
for configuration details including how to disable the use of these servers or use
different ones.
</p>
<p> <p>
TODO TODO
</p> </p>
@ -31,13 +41,74 @@ TODO
<h2 id="language">Changes to the language</h2> <h2 id="language">Changes to the language</h2>
<p> <p>
TODO Per the <a href="https://github.com/golang/proposal/blob/master/design/19308-number-literals.md">number literal proposal</a>,
Go 1.13 supports a more uniform and modernized set of number literal prefixes.
<ul>
<li>
<a href="https://golang.org/ref/spec#Integer_literals">Binary integer literals</a>:
The prefix <code>0b</code> or <code>0B</code> indicates a binary integer literal
such as <code>0b1011</code>.
</li>
<li>
<a href="https://golang.org/ref/spec#Integer_literals">Octal integer literals</a>:
The prefix <code>0o</code> or <code>0O</code> indicates an octal integer literal
such as <code>0o660</code>.
The existing octal notation indicated by a leading <code>0</code> followed by
octal digits remains valid.
</li>
<li>
<a href="https://golang.org/ref/spec#Floating-point_literals">Hexadecimal floating point literals</a>:
The prefix <code>0x</code> or <code>0X</code> may now be used to express the mantissa of a
floating-point number in hexadecimal format such as <code>0x1.0p-1021</code>.
A hexadecimal floating-point number must always have an exponent, written as the letter
<code>p</code> or <code>P</code> followed by an exponent in decimal. The exponent scales
the mantissa by 2 to the power of the exponent.
</li>
<li>
<a href="https://golang.org/ref/spec#Imaginary_literals">Imaginary literals</a>:
The imaginary suffix <code>i</code> may now be used with any (binary, decimal, hexadecimal)
integer or floating-point literal.
</li>
<li>
Digit separators:
The digits of any number literal may now be separated (grouped) using underscores, such as
in <code>1_000_000</code>, <code>0b_1010_0110</code>, or <code>3.1415_9265</code>.
An underscore may appear between any two digits or the literal prefix and the first digit.
</li>
</ul>
</p>
<p>
Per the <a href="https://github.com/golang/proposal/blob/master/design/19113-signed-shift-counts.md">signed shift counts proposal</a>
Go 1.13 removes the restriction that a <a href="https://golang.org/ref/spec#Operators">shift count</a>
must be unsigned. This change eliminates the need for many artificial <code>uint</code> conversions,
solely introduced to satisfy this (now removed) restriction of the <code>&lt;&lt;</code> and <code>&gt;&gt;</code> operators.
</p>
<p>
These language changes were implemented by changes to the compiler, and corresponding internal changes to the library
packages <code><a href="https://golang.org/pkg/go/scanner">go/scanner</a></code> and
<code><a href="https://golang.org/pkg/text/scanner">text/scanner</a></code> (number literals),
and <code><a href="https://golang.org/pkg/go/types">go/types</a></code> (signed shift counts).
</p>
<p>
If your code uses modules and your <code>go.mod</code> files specifies a language version, be sure
it is set to at least <code>1.13</code> to get access to these language changes.
You can do this by editing the <code>go.mod</code> file directly, or you can run
<code>go mod edit -go=1.13</code>.
</p> </p>
<h2 id="ports">Ports</h2> <h2 id="ports">Ports</h2>
<p> <h3 id="android">Android</h3>
TODO
<p><!-- CL 170127 -->
Go programs are now compatible with Android Q.
</p> </p>
<h3 id="darwin">Darwin</h3> <h3 id="darwin">Darwin</h3>
@ -57,18 +128,92 @@ TODO
FreeBSD 12.0 or later requires a kernel with the COMPAT_FREEBSD11 option set (this is the default). FreeBSD 12.0 or later requires a kernel with the COMPAT_FREEBSD11 option set (this is the default).
</p> </p>
<h3 id="illumos">Illumos</h3>
<p><!-- CL 174457 -->
Go now supports Illumos with <code>GOOS=illumos</code>.
The <code>illumos</code> build tag implies the <code>solaris</code>
build tag.
</p>
<h3 id="aix">AIX</h3>
<p><!-- CL 164003, CL 169120 -->
AIX on PPC64 (<code>aix/ppc64</code>) now supports cgo, external
linking, and the <code>c-archive</code> and <code>pie</code> build
modes.
</p>
<h3 id="windows">Windows</h3>
<p><!-- CL 178977 -->
The Windows version specified by internally-linked Windows binaries
is now Windows 7 rather than NT 4.0. This was already the minimum
required version for Go, but can affect the behavior of system calls
that have a backwards-compatibility mode. These will now behave as
documented. Externally-linked binaries (any program using cgo) have
always specified a more recent Windows version.
</p>
<h2 id="tools">Tools</h2> <h2 id="tools">Tools</h2>
<p> <p>
TODO TODO
</p> </p>
<h2 id="runtime">Runtime</h2> <h3 id="compiler">Compiler toolchain</h3>
<p> <p><!-- CL 170448 -->
TODO The compiler has a new implementation of escape analysis that is
more precise. For most Go code should be an improvement (in other
words, more Go variables and expressions allocated on the stack
instead of heap). However, this increased precision may also break
invalid code that happened to work before (for example, code that
violates
the <a href="/pkg/unsafe/#Pointer"><code>unsafe.Pointer</code>
safety rules</a>). If you notice any regressions that appear
related, the old escape analysis pass can be re-enabled
with <code>go</code> <code>build</code> <code>-gcflags=all=-newescape=false</code>.
The option to use the old escape analysis will be removed in a
future release.
</p> </p>
<p><!-- CL 161904 -->
The compiler no longer emits floating point or complex constants
to <code>go_asm.h</code> files. These have always been emitted in a
form that could not be used as numeric constant in assembly code.
</p>
<h3 id="assembler">Assembler</h3>
<p><!-- CL 157001 -->
The assembler now supports many of the atomic instructions
introduced in ARM v8.1.
</p>
<h2 id="runtime">Runtime</h2>
<p><!-- CL 161477 -->
Out of range panic messages now include the index that was out of
bounds and the length (or capacity) of the slice. For
example, <code>s[3]</code> on a slice of length 1 will panic with
"runtime error: index out of range [3] with length 1".
</p>
<p><!-- CL 171758 -->
This release improves performance of most uses of <code>defer</code>
by 30%.
</p>
<p><!-- CL 142960 -->
The runtime is now more aggressive at returning memory to the
operating system to make it available to co-tenant applications.
Previously, the runtime could retain memory for five or more minutes
following a spike in the heap size. It will now begin returning it
promptly after the heap shrinks. However, on many OSes, including
Linux, the OS itself reclaims memory lazily, so process RSS will not
decrease until the system is under memory pressure.
</p>
<h2 id="library">Core library</h2> <h2 id="library">Core library</h2>
@ -79,9 +224,27 @@ TODO generally
<h3 id="tls_1_3">TLS 1.3</h3> <h3 id="tls_1_3">TLS 1.3</h3>
<p> <p>
TODO; link to <a href="/doc/go1.12#tls_1_3">Go 1.12 notes</a>. As announced in Go 1.12, Go 1.13 enables support for TLS 1.3 in the
<code>crypto/tls</code> package by default. It can be disabled by adding the
value <code>tls13=0</code> to the <code>GODEBUG</code>
environment variable. The opt-out will be removed in Go 1.14.
</p> </p>
<p>
See <a href="/doc/go1.12#tls_1_3">the Go 1.12 release notes</a> for important
compatibility information.
</p>
<h3 id="crypto/ed25519"><a href="/pkg/crypto/ed25519/">crypto/ed25519</a></h3>
<p><!-- CL 174945, 182698 -->
The new <a href="/pkg/crypto/ed25519/"><code>crypto/ed25519</code></a>
package implements the Ed25519 signature
scheme. This functionality was previously provided by the
<a href="https://godoc.org/golang.org/x/crypto/ed25519"><code>golang.org/x/crypto/ed25519</code></a>
package, which becomes a wrapper for
<code>crypto/ed25519</code> when used with Go 1.13+.
</p>
<h3 id="minor_library_changes">Minor changes to the library</h3> <h3 id="minor_library_changes">Minor changes to the library</h3>
@ -95,3 +258,312 @@ TODO; link to <a href="/doc/go1.12#tls_1_3">Go 1.12 notes</a>.
TODO TODO
</p> </p>
<!-- CL 174125: https://golang.org/cl/174125: cmd/dist: add support for openbsd/arm64 -->
<!-- CL 177797: https://golang.org/cl/177797: cmd/doc: always print package clause except for commands -->
<!-- CL 173345: https://golang.org/cl/173345: cmd/go: add -trimpath build flag -->
<!-- CL 173438: https://golang.org/cl/173438: cmd/go: change -tags to a comma-separated list -->
<!-- CL 175983: https://golang.org/cl/175983: cmd/go: set the "generate" build tag in go generate, per design doc -->
<!-- CL 167747: https://golang.org/cl/167747: 'go get' in module mode now supports the version suffix '@patch'.: cmd/go/internal/modget: support the suffix '@patch' in 'go get' -->
<dl id="bytes"><dt><a href="/pkg/bytes/">bytes</a></dt>
<dd>
<p><!-- CL 161760 -->
TODO: <a href="https://golang.org/cl/161760">https://golang.org/cl/161760</a>: hoist error creation out of function
</p>
<p>
The new <a href="/pkg/bytes/#ToValidUTF8"><code>ToValidUTF8</code></a> function returns a
copy of a given byte slice with each run of invalid UTF-8 byte sequences replaced by a given slice.
</p>
</dl><!-- bytes -->
<dl id="context"><dt><a href="/pkg/context/">context</a></dt>
<dd>
<p><!-- CL 169080 -->
The formatting of contexts returned by <a href="/pkg/context/#WithValue"><code>WithValue</code></a> no longer depends on <code>fmt</code> and will not stringify in the same way. Code that depends on the exact previous stringification might be affected.
</p>
</dl><!-- context -->
<dl id="crypto/tls"><dt><a href="/pkg/crypto/tls/">crypto/tls</a></dt>
<dd>
<p><!-- CL 177698 -->
Ed25519 certificates are now supported in TLS versions 1.2 and 1.3.
</p>
</dl><!-- crypto/tls -->
<dl id="crypto/x509"><dt><a href="/pkg/crypto/x509/">crypto/x509</a></dt>
<dd>
<p><!-- CL 175478 -->
Ed25519 keys are now supported in certificates and certificate requests
according to <a href="https://www.rfc-editor.org/info/rfc8410">RFC 8410</a>, as well as by the
<a href="/pkg/crypto/x509/#ParsePKCS8PrivateKey"><code>ParsePKCS8PrivateKey</code></a>,
<a href="/pkg/crypto/x509/#MarshalPKCS8PrivateKey"><code>MarshalPKCS8PrivateKey</code></a>,
and <a href="/pkg/crypto/x509/#ParsePKIXPublicKey"><code>ParsePKIXPublicKey</code></a> functions.
</p>
</dl><!-- crypto/x509 -->
<dl id="database/sql"><dt><a href="/pkg/database/sql/">database/sql</a></dt>
<dd>
<p><!-- CL 170699 -->
The new <a href="/pkg/database/sql/#NullTime"><code>NullTime</code></a> type represents a <code>time.Time</code> that may be null.
</p>
<p><!-- CL 174178 -->
The new <a href="/pkg/database/sql/#NullInt32"><code>NullInt32</code></a> type represents an <code>int32</code> that may be null.
</p>
</dl><!-- database/sql -->
<dl id="debug/dwarf"><dt><a href="/pkg/debug/dwarf/">debug/dwarf</a></dt>
<dd>
<p><!-- CL 158797 -->
The <a href="/pkg/debug/dwarf/#Data.Type"><code>Data.Type</code></a>
method no longer panics if it encounters an unknown DWARF tag in
the type graph. Instead, it represents that component of the
type with
an <a href="/pkg/debug/dwarf/#UnsupportedType"><code>UnsupportedType</code></a>
object.
</p>
</dl><!-- debug/dwarf -->
<dl id="html/template"><dt><a href="/pkg/html/template/">html/template</a></dt>
<dd>
<p><!-- CL 175218 -->
When using a <code>&lt;script&gt;</code> tag with "module" set as the
type attribute, code will now be interperted as <a href="https://html.spec.whatwg.org/multipage/scripting.html#the-script-element:module-script-2">JavaScript module script</a>.
</p>
</dl><!-- html/template -->
<dl id="log"><dt><a href="/pkg/log/">log</a></dt>
<dd>
<p><!-- CL 168920 -->
The new <a href="/pkg/log/#Writer"><code>Writer</code></a> function returns the output destination for the standard logger.
</p>
</dl><!-- log -->
<dl id="math/big"><dt><a href="/pkg/math/big/">math/big</a></dt>
<dd>
<p><!-- CL 160682 -->
TODO: <a href="https://golang.org/cl/160682">https://golang.org/cl/160682</a>: implement Rat.SetUint64
</p>
<p><!-- CL 168237 -->
TODO: <a href="https://golang.org/cl/168237">https://golang.org/cl/168237</a>: accept non-decimal floats with Rat.SetString
</p>
</dl><!-- math/big -->
<dl id="math/bits"><dt><a href="/pkg/math/bits/">math/bits</a></dt>
<dd>
<p><!-- CL 178177 -->
The execution time of <a href="/pkg/math/bits/#Add"><code>Add</code></a>,
<a href="/pkg/math/bits/#Sub"><code>Sub</code></a>,
<a href="/pkg/math/bits/#Mul"><code>Mul</code></a>,
<a href="/pkg/math/bits/#RotateLeft"><code>RotateLeft</code></a>, and
<a href="/pkg/math/bits/#ReverseBytes"><code>ReverseBytes</code></a> is now
guaranteed to be independent of the inputs.
</p>
</dl><!-- math/bits -->
<dl id="net"><dt><a href="/pkg/net/">net</a></dt>
<dd>
<p><!-- CL 156366 -->
On Unix systems where <code>use-vc</code> is set in <code>resolve.conf</code>, TCP is used for DNS resolution.
</p>
<p><!-- CL 170678 -->
The new field <a href="/pkg/net/#ListenConfig.KeepAlive"><code>ListenConfig.KeepAlive</code></a>
specifies the keep-alive period for network connections accepted by the listener.
</p>
</dl><!-- net -->
<dl id="net/http"><dt><a href="/pkg/net/http/">net/http</a></dt>
<dd>
<p><!-- CL 130256 -->
The new field <a href="/pkg/net/http/#Transport.ForceAttemptHTTP2"><code>Transport.ForceAttemptHTTP2</code></a>
controls whether HTTP/2 is enabled when a non-zero <code>Dial</code>, <code>DialTLS</code>, or <code>DialContext</code>
func or <code>TLSClientConfig</code> is provided.
</p>
<p><!-- CL 140357 -->
When reusing HTTP/2, the <a href="/pkg/net/http#Transport"><code>Transport</code></a> no longer performs unnecessary TLS handshakes.
</p>
<p><!-- CL 154383 -->
<a href="/pkg/net/http/#TimeoutHandler"><code>TimeoutHandler</code></a>'s
<a href="/pkg/net/http/#ResponseWriter"><code>ResponseWriter</code></a> now implements the
<a href="/pkg/net/http/#Pusher"><code>Pusher</code></a> and <a href="/pkg/net/http/#Flusher"><code>Flusher</code></a> interfaces.
</p>
<p><!-- CL 167681 -->
The new <a href="/pkg/net/http#Server"><code>Server</code></a> fields
<a href="/pkg/net/http/#Server.BaseContext"><code>BaseContext</code></a> and
<a href="/pkg/net/http/#Server.ConnContext"><code>ConnContext</code></a>
allow finer control over the <a href="/pkg/context#Context"><code>Context</code></a> values provided to requests and connections.
</p>
<p><!-- CL 173658 -->
The new <a href="/pkg/net/http/#Header"><code>Header</code></a> method
<a href="/pkg/net/http/#Header.Clone"><code>Clone</code></a> returns a copy of the receiver.
</p>
</dl><!-- net/http -->
<dl id="os"><dt><a href="/pkg/os/">os</a></dt>
<dd>
<p><!-- CL 160877 -->
The new <a href="/pkg/os/#UserConfigDir"><code>UserConfigDir</code></a> function
returns the default directory to use for user-specific configuration data.
</p>
<p><!-- CL 166578 -->
If a <a href="/pkg/os/#File"><code>File</code></a> is opened using the O_APPEND flag, its
<a href="/pkg/os/#File.WriteAt"><code>WriteAt</code></a> method will always return an error.
</p>
</dl><!-- os -->
<dl id="os/exec"><dt><a href="/pkg/os/exec/">os/exec</a></dt>
<dd>
<p><!-- CL 174318 -->
On Windows, the environment for a <a href="/pkg/os/exec#Cmd"><code>Cmd</code></a> always inherits the
<code>%SYSTEMROOT%</code> value of the parent process unless the
<a href="/pkg/os/exec#Cmd.Env"><code>Cmd.Env</code></a> field includes an explicit value for it.
</p>
</dl><!-- os/exec -->
<dl id="reflect"><dt><a href="/pkg/reflect/">reflect</a></dt>
<dd>
<p><!-- CL 171337 -->
The new <a href="/pkg/reflect/#Value.IsZero"><code>Value.IsZero</code></a> method reports whether a <code>Value</code> is the zero value for its type.
</p>
<p><!-- CL 174531 -->
The <a href="/pkg/reflect/#MakeFunc"><code>MakeFunc</code></a> function now allows assignment conversions on returned values, instead of requiring exact type match. This is particularly useful when the type being returned is an interface type, but the value actually returned is a concrete value implementing that type.
</p>
</dl><!-- reflect -->
<dl id="runtime"><dt><a href="/pkg/runtime/">runtime</a></dt>
<dd>
<p> <!-- CL 167780 -->
Tracebacks, <code>runtime.Caller</code>,
and <code>runtime.Callers</code> now refer to the function that
initializes the global variables of <code>PKG</code>
as <code>PKG.init</code> instead of <code>PKG.init.ializers</code>
</p>
</dl><!-- runtime -->
<dl id="strings"><dt><a href="/pkg/strings">strings</a></dt>
<dd>
<p><!-- CL 142003 -->
The new <a href="/pkg/strings/#ToValidUTF8"><code>ToValidUTF8</code></a> function returns a
copy of a given string with each run of invalid UTF-8 byte sequences replaced by a given string.
</p>
</dl><!-- strings -->
<dl id="sync"><dt><a href="/pkg/sync/">sync</a></dt>
<dd>
<p><!-- CL 166960 -->
Large <a href="/pkg/sync/#Pool"><code>Pool</code></a> no longer increase stop-the-world pause times.
</p>
<p><!-- CL 166961 -->
<code>Pool</code> no longer needs to be completely repopulated after every GC. It now retains some objects across GCs,
as opposed to releasing all objects, reducing load spikes for heavy users of <code>Pool</code>.
</p>
</dl><!-- sync -->
<dl id="syscall"><dt><a href="/pkg/syscall/">syscall</a></dt>
<dd>
<p><!-- CL 168479 -->
Uses of <code>_getdirentries64</code> have been removed from Darwin builds, to allow binaries
built with 1.12 to be uploaded to the macOS App Store.
</p>
<p><!-- CL 174197 -->
The new <code>ProcessAttributes</code> and <code>ThreadAttributes</code> fields in
<a href="/pkg/syscall/?GOOS=windows#SysProcAttr"><code>SysProcAttr</code></a> have been introduced for Windows,
exposing security settings when creating new processes.
</p>
<p><!-- CL 174320 -->
<code>EINVAL</code> is no longer returned in zero
<a href="/pkg/syscall/?GOOS=windows#Chmod"><code>Chmod</code></a> mode on Windows.
</p>
</dl><!-- syscall -->
<dl id="syscall/js"><dt><a href="/pkg/syscall/js/">syscall/js</a></dt>
<dd>
<p><!-- CL 177537 -->
<a href="/pkg/syscall/js/#TypedArray"><code>TypedArrayOf</code></a> has been replaced by
<a href="/pkg/syscall/js/#CopyBytesToGo"><code>CopyBytesToGo</code></a> and
<a href="/pkg/syscall/js/#CopyBytesToJS"><code>CopyBytesToJS</code></a> for copying bytes between a byte slice and a Uint8Array.
</p>
</dl><!-- syscall/js -->
<dl id="testing"><dt><a href="/pkg/testing/">testing</a></dt>
<dd>
<p><!-- CL 112155 -->
TODO: <a href="https://golang.org/cl/112155">https://golang.org/cl/112155</a>: stop rounding b.N
</p>
<p><!-- CL 166717 -->
TODO: <a href="https://golang.org/cl/166717">https://golang.org/cl/166717</a>: add B.ReportMetric for custom benchmark metrics
</p>
<p><!-- CL 173722 -->
TODO: <a href="https://golang.org/cl/173722">https://golang.org/cl/173722</a>: delay flag registration; move to an Init function
</p>
</dl><!-- testing -->
<dl id="text/scanner"><dt><a href="/pkg/text/scanner/">text/scanner</a></dt>
<dd>
<p><!-- CL 163079 -->
The new <a href="/pkg/text/scanner/#AllowNumberbars"><code>AllowNumberbars</code></a>
mode allows number literals to contain underbars as digit separators.
</p>
</dl><!-- text/scanner -->
<dl id="text/template"><dt><a href="/pkg/text/template/">text/template</a></dt>
<dd>
<p><!-- CL 161762 -->
The new <a href="/pkg/text/template/#hdr-Functions">slice function</a>
returns the result of slicing its first argument by the following arguments.
</p>
</dl><!-- text/template -->
<dl id="time"><dt><a href="/pkg/time/">time</a></dt>
<dd>
<p><!-- CL 122876 -->
Day-of-year is now supported by <a href="/pkg/time/#time.Format"><code>Format</code></a>
and <a href="/pkg/time/#Parse"><code>Parse</code></a>.
</p>
<p><!-- CL 167387 -->
The new <a href="/pkg/time/#Duration"><code>Duration</code></a> methods
<a href="/pkg/time/#Duration.Microseconds"><code>Microseconds</code></a> and
<a href="/pkg/time/#Duration.Milliseconds"><code>Milliseconds</code></a> return
the duration as an integer count of their respectively named units.
</p>
</dl><!-- time -->

View file

@ -59,9 +59,6 @@ for important announcements, such as the availability of new Go releases.
<h3 id="twitter"><a href="https://twitter.com/golang">@golang at Twitter</a></h3> <h3 id="twitter"><a href="https://twitter.com/golang">@golang at Twitter</a></h3>
<p>The Go project's official Twitter account.</p> <p>The Go project's official Twitter account.</p>
<h3 id="pluscom"><a href="https://plus.google.com/communities/114112804251407510571">Go+ community</a></h3>
<p>A Google+ community for Go enthusiasts.</p>
<h3 id="reddit"><a href="https://reddit.com/r/golang">golang sub-Reddit</a></h3> <h3 id="reddit"><a href="https://reddit.com/r/golang">golang sub-Reddit</a></h3>
<p> <p>
The <a href="https://reddit.com/r/golang">golang sub-Reddit</a> is a place The <a href="https://reddit.com/r/golang">golang sub-Reddit</a> is a place

View file

@ -218,15 +218,14 @@ To build without <code>cgo</code>, set the environment variable
<h2 id="fetch">Fetch the repository</h2> <h2 id="fetch">Fetch the repository</h2>
<p>Go will install to a directory named <code>go</code>. <p>Change to the directory where you intend to install Go, and make sure
Change to the directory that will be its parent the <code>goroot</code> directory does not exist. Then clone the repository
and make sure the <code>go</code> directory does not exist. and check out the latest release tag (<code class="versionTag">go1.12</code>,
Then clone the repository and check out the latest release tag for example):</p>
(<code class="versionTag">go1.9</code>, for example):</p>
<pre> <pre>
$ git clone https://go.googlesource.com/go $ git clone https://go.googlesource.com/go goroot
$ cd go $ cd goroot
$ git checkout <span class="versionTag"><i>&lt;tag&gt;</i></span> $ git checkout <span class="versionTag"><i>&lt;tag&gt;</i></span>
</pre> </pre>
@ -234,6 +233,13 @@ $ git checkout <span class="versionTag"><i>&lt;tag&gt;</i></span>
Where <code>&lt;tag&gt;</code> is the version string of the release. Where <code>&lt;tag&gt;</code> is the version string of the release.
</p> </p>
<p>Go will be installed in the directory where it is checked out. For example,
if Go is checked out in <code>$HOME/goroot</code>, executables will be installed
in <code>$HOME/goroot/bin</code>. The directory may have any name, but note
that if Go is checked out in <code>$HOME/go</code>, it will conflict with
the default location of <code>$GOPATH</code>.
See <a href="#gopath"><code>GOPATH</code></a> below.</p>
<h2 id="head">(Optional) Switch to the master branch</h2> <h2 id="head">(Optional) Switch to the master branch</h2>
<p>If you intend to modify the go source code, and <p>If you intend to modify the go source code, and
@ -441,6 +447,43 @@ but move it elsewhere after the build, set
</p> </p>
</li> </li>
<li id="gopath"><code>$GOPATH</code>
<p>
The directory where Go projects outside the Go distribution are typically
checked out. For example, <code>golang.org/x/tools</code> might be checked out
to <code>$GOPATH/src/golang.org/x/tools</code>. Executables outside the
Go distribution are installed in <code>$GOPATH/bin</code> (or
<code>$GOBIN</code>, if set). Modules are downloaded and cached in
<code>$GOPATH/pkg/mod</code>.
</p>
<p>The default location of <code>$GOPATH</code> is <code>$HOME/go</code>,
and it's not usually necessary to set <code>GOPATH</code> explicitly. However,
if you have checked out the Go distribution to <code>$HOME/go</code>,
you must set <code>GOPATH</code> to another location to avoid conflicts.
</p>
</li>
<li><code>$GOBIN</code>
<p>
The directory where executables outside the Go distribution are installed
using the <a href="/cmd/go">go command</a>. For example,
<code>go get golang.org/x/tools/cmd/godoc</code> downloads, builds, and
installs <code>$GOBIN/godoc</code>. By default, <code>$GOBIN</code> is
<code>$GOPATH/bin</code> (or <code>$HOME/go/bin</code> if <code>GOPATH</code>
is not set). After installing, you will want to add this directory to
your <code>$PATH</code> so you can use installed tools.
</p>
<p>
Note that the Go distribution's executables are installed in
<code>$GOROOT/bin</code> (for executables invoked by people) or
<code>$GOTOOLDIR</code> (for executables invoked by the go command;
defaults to <code>$GOROOT/pkg/$GOOS_GOARCH</code>) instead of
<code>$GOBIN</code>.
</p>
</li>
<li><code>$GOOS</code> and <code>$GOARCH</code> <li><code>$GOOS</code> and <code>$GOARCH</code>
<p> <p>
The name of the target operating system and compilation architecture. The name of the target operating system and compilation architecture.
@ -577,17 +620,6 @@ For example, you should not set <code>$GOHOSTARCH</code> to
<code>arm</code> on an x86 system. <code>arm</code> on an x86 system.
</p> </p>
<li><code>$GOBIN</code>
<p>
The location where Go binaries will be installed.
The default is <code>$GOROOT/bin</code>.
After installing, you will want to arrange to add this
directory to your <code>$PATH</code>, so you can use the tools.
If <code>$GOBIN</code> is set, the <a href="/cmd/go">go command</a>
installs all commands there.
</p>
</li>
<li><code>$GO386</code> (for <code>386</code> only, default is auto-detected <li><code>$GO386</code> (for <code>386</code> only, default is auto-detected
if built on either <code>386</code> or <code>amd64</code>, <code>387</code> otherwise) if built on either <code>386</code> or <code>amd64</code>, <code>387</code> otherwise)
<p> <p>

View file

@ -440,7 +440,6 @@ func TestPointerChecks(t *testing.T) {
atomic.AddInt32(&pending, +1) atomic.AddInt32(&pending, +1)
defer func() { defer func() {
if atomic.AddInt32(&pending, -1) == 0 { if atomic.AddInt32(&pending, -1) == 0 {
println("removing", dir)
os.RemoveAll(dir) os.RemoveAll(dir)
} }
}() }()
@ -554,18 +553,23 @@ func main() {
} }
` `
var csem = make(chan bool, 16)
func testOne(t *testing.T, pt ptrTest, exe string) { func testOne(t *testing.T, pt ptrTest, exe string) {
t.Parallel() t.Parallel()
newcmd := func(cgocheck string) *exec.Cmd { // Run the tests in parallel, but don't run too many
// executions in parallel, to avoid overloading the system.
runcmd := func(cgocheck string) ([]byte, error) {
csem <- true
defer func() { <-csem }()
cmd := exec.Command(exe, pt.name) cmd := exec.Command(exe, pt.name)
cmd.Env = append(os.Environ(), "GODEBUG=cgocheck="+cgocheck) cmd.Env = append(os.Environ(), "GODEBUG=cgocheck="+cgocheck)
return cmd return cmd.CombinedOutput()
} }
if pt.expensive { if pt.expensive {
cmd := newcmd("1") buf, err := runcmd("1")
buf, err := cmd.CombinedOutput()
if err != nil { if err != nil {
t.Logf("%s", buf) t.Logf("%s", buf)
if pt.fail { if pt.fail {
@ -577,12 +581,12 @@ func testOne(t *testing.T, pt ptrTest, exe string) {
} }
cmd := newcmd("") cgocheck := ""
if pt.expensive { if pt.expensive {
cmd = newcmd("2") cgocheck = "2"
} }
buf, err := cmd.CombinedOutput() buf, err := runcmd(cgocheck)
if pt.fail { if pt.fail {
if err == nil { if err == nil {
t.Logf("%s", buf) t.Logf("%s", buf)
@ -599,8 +603,7 @@ func testOne(t *testing.T, pt ptrTest, exe string) {
if !pt.expensive { if !pt.expensive {
// Make sure it passes with the expensive checks. // Make sure it passes with the expensive checks.
cmd := newcmd("2") buf, err := runcmd("2")
buf, err := cmd.CombinedOutput()
if err != nil { if err != nil {
t.Logf("%s", buf) t.Logf("%s", buf)
t.Fatalf("failed unexpectedly with expensive checks: %v", err) t.Fatalf("failed unexpectedly with expensive checks: %v", err)
@ -609,8 +612,7 @@ func testOne(t *testing.T, pt ptrTest, exe string) {
} }
if pt.fail { if pt.fail {
cmd := newcmd("0") buf, err := runcmd("0")
buf, err := cmd.CombinedOutput()
if err != nil { if err != nil {
t.Logf("%s", buf) t.Logf("%s", buf)
t.Fatalf("failed unexpectedly with GODEBUG=cgocheck=0: %v", err) t.Fatalf("failed unexpectedly with GODEBUG=cgocheck=0: %v", err)

View file

@ -56,7 +56,6 @@ func Test25143(t *testing.T) { test25143(t) }
func Test26066(t *testing.T) { test26066(t) } func Test26066(t *testing.T) { test26066(t) }
func Test27660(t *testing.T) { test27660(t) } func Test27660(t *testing.T) { test27660(t) }
func Test28896(t *testing.T) { test28896(t) } func Test28896(t *testing.T) { test28896(t) }
func Test29878(t *testing.T) { test29878(t) }
func Test30065(t *testing.T) { test30065(t) } func Test30065(t *testing.T) { test30065(t) }
func TestAlign(t *testing.T) { testAlign(t) } func TestAlign(t *testing.T) { testAlign(t) }
func TestAtol(t *testing.T) { testAtol(t) } func TestAtol(t *testing.T) { testAtol(t) }

View file

@ -849,9 +849,8 @@ static int f29748(S29748 *p) { return 0; }
static void issue29781F(char **p, int n) {} static void issue29781F(char **p, int n) {}
#define ISSUE29781C 0 #define ISSUE29781C 0
// issue 29878 // issue 31093
uint64_t issue29878exported(int8_t); // prototype must match static uint16_t issue31093F(uint16_t v) { return v; }
int16_t issue29878function(uint32_t arg) { return issue29878exported(arg); }
*/ */
import "C" import "C"
@ -2054,14 +2053,6 @@ func issue29781G() {
X)) X))
} }
func test29878(t *testing.T) {
const arg uint32 = 123 // fits into all integer types
var ret int16 = C.issue29878function(arg) // no conversions needed
if int64(ret) != int64(arg) {
t.Errorf("return value unexpected: got %d, want %d", ret, arg)
}
}
// issue 30065 // issue 30065
func test30065(t *testing.T) { func test30065(t *testing.T) {
@ -2085,3 +2076,10 @@ func test30065(t *testing.T) {
t.Errorf("&d[0] failed: got %c, want %c", d[0], 'c') t.Errorf("&d[0] failed: got %c, want %c", d[0], 'c')
} }
} }
// issue 31093
// No runtime test; just make sure it compiles.
func Issue31093() {
C.issue31093F(C.ushort(0))
}

View file

@ -535,8 +535,3 @@ func test20910(t *testing.T) {
// issue 28772 part 2 // issue 28772 part 2
const issue28772Constant2 = C.issue28772Constant2 const issue28772Constant2 = C.issue28772Constant2
//export issue29878exported
func issue29878exported(arg int8) uint64 {
return uint64(arg)
}

View file

@ -5,15 +5,11 @@
package sanitizers_test package sanitizers_test
import ( import (
"runtime"
"strings" "strings"
"testing" "testing"
) )
func TestTSAN(t *testing.T) { func TestTSAN(t *testing.T) {
if runtime.GOARCH == "arm64" {
t.Skip("skipping test; see https://golang.org/issue/25682")
}
t.Parallel() t.Parallel()
requireOvercommit(t) requireOvercommit(t)
config := configure("thread") config := configure("thread")

View file

@ -40,7 +40,7 @@ To use the go tool directly to run programs and tests, put $GOROOT/bin into PATH
the go_darwin_$GOARCH_exec wrapper is found. For example, to run the archive/tar tests the go_darwin_$GOARCH_exec wrapper is found. For example, to run the archive/tar tests
export PATH=$GOROOT/bin:$PATH export PATH=$GOROOT/bin:$PATH
GOARCH=arm64 go test archive/tar GOARCH=arm64 CGO_ENABLED=1 go test archive/tar
Note that the go_darwin_$GOARCH_exec wrapper uninstalls any existing app identified by Note that the go_darwin_$GOARCH_exec wrapper uninstalls any existing app identified by
the bundle id before installing a new app. If the uninstalled app is the last app by the bundle id before installing a new app. If the uninstalled app is the last app by

View file

@ -387,6 +387,34 @@
mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16)); mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16));
}, },
// func copyBytesToGo(dst []byte, src ref) (int, bool)
"syscall/js.copyBytesToGo": (sp) => {
const dst = loadSlice(sp + 8);
const src = loadValue(sp + 32);
if (!(src instanceof Uint8Array)) {
mem().setUint8(sp + 48, 0);
return;
}
const toCopy = src.subarray(0, dst.length);
dst.set(toCopy);
setInt64(sp + 40, toCopy.length);
mem().setUint8(sp + 48, 1);
},
// func copyBytesToJS(dst ref, src []byte) (int, bool)
"syscall/js.copyBytesToJS": (sp) => {
const dst = loadValue(sp + 8);
const src = loadSlice(sp + 16);
if (!(dst instanceof Uint8Array)) {
mem().setUint8(sp + 48, 0);
return;
}
const toCopy = src.subarray(0, dst.length);
dst.set(toCopy);
setInt64(sp + 40, toCopy.length);
mem().setUint8(sp + 48, 1);
},
"debug": (value) => { "debug": (value) => {
console.log(value); console.log(value);
}, },
@ -403,7 +431,6 @@
true, true,
false, false,
global, global,
this._inst.exports.mem,
this, this,
]; ];
this._refs = new Map(); this._refs = new Map();

View file

@ -21,9 +21,6 @@ type Buffer struct {
buf []byte // contents are the bytes buf[off : len(buf)] buf []byte // contents are the bytes buf[off : len(buf)]
off int // read at &buf[off], write at &buf[len(buf)] off int // read at &buf[off], write at &buf[len(buf)]
lastRead readOp // last read operation, so that Unread* can work correctly. lastRead readOp // last read operation, so that Unread* can work correctly.
// FIXME: it would be advisable to align Buffer to cachelines to avoid false
// sharing.
} }
// The readOp constants describe the last action performed on // The readOp constants describe the last action performed on

View file

@ -148,8 +148,6 @@ 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). C.complexfloat (complex float), and C.complexdouble (complex double).
The C type void* is represented by Go's unsafe.Pointer. The C type void* is represented by Go's unsafe.Pointer.
The C sized integer types (int8_t, uint8_t, ) are represented by their Go
counterparts (int8, uint8, ).
The C types __int128_t and __uint128_t are represented by [16]byte. The C types __int128_t and __uint128_t are represented by [16]byte.
A few special C types which would normally be represented by a pointer A few special C types which would normally be represented by a pointer
@ -298,7 +296,7 @@ Go functions can be exported for use by C code in the following way:
They will be available in the C code as: They will be available in the C code as:
extern int64_t MyFunction(int arg1, int arg2, GoString arg3); extern GoInt64 MyFunction(int arg1, int arg2, GoString arg3);
extern struct MyFunction2_return MyFunction2(int arg1, int arg2, GoString arg3); extern struct MyFunction2_return MyFunction2(int arg1, int arg2, GoString arg3);
found in the _cgo_export.h generated header, after any preambles found in the _cgo_export.h generated header, after any preambles

View file

@ -23,7 +23,6 @@ import (
"internal/xcoff" "internal/xcoff"
"math" "math"
"os" "os"
"regexp"
"strconv" "strconv"
"strings" "strings"
"unicode" "unicode"
@ -2047,8 +2046,6 @@ type typeConv struct {
ptrSize int64 ptrSize int64
intSize int64 intSize int64
exactWidthIntegerTypes map[string]*Type
} }
var tagGen int var tagGen int
@ -2091,21 +2088,6 @@ func (c *typeConv) Init(ptrSize, intSize int64) {
} else { } else {
c.goVoidPtr = c.Ident("unsafe.Pointer") c.goVoidPtr = c.Ident("unsafe.Pointer")
} }
c.exactWidthIntegerTypes = make(map[string]*Type)
for _, t := range []ast.Expr{
c.int8, c.int16, c.int32, c.int64,
c.uint8, c.uint16, c.uint32, c.uint64,
} {
name := t.(*ast.Ident).Name
u := new(Type)
*u = *goTypes[name]
if u.Align > ptrSize {
u.Align = ptrSize
}
u.Go = t
c.exactWidthIntegerTypes[name] = u
}
} }
// base strips away qualifiers and typedefs to get the underlying type // base strips away qualifiers and typedefs to get the underlying type
@ -2477,26 +2459,6 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
t.Align = c.ptrSize t.Align = c.ptrSize
break break
} }
// Exact-width integer types. These are always compatible with
// the corresponding Go types since the C standard requires
// them to have no padding bit and use the twos complement
// representation.
if exactWidthIntegerType.MatchString(dt.Name) {
sub := c.Type(dt.Type, pos)
goname := strings.TrimPrefix(dt.Name, "__")
goname = strings.TrimSuffix(goname, "_t")
u := c.exactWidthIntegerTypes[goname]
if sub.Size != u.Size {
fatalf("%s: unexpected size: %d vs. %d %s", lineno(pos), sub.Size, u.Size, dtype)
}
if sub.Align != u.Align {
fatalf("%s: unexpected alignment: %d vs. %d %s", lineno(pos), sub.Align, u.Align, dtype)
}
t.Size = u.Size
t.Align = u.Align
t.Go = u.Go
break
}
name := c.Ident("_Ctype_" + dt.Name) name := c.Ident("_Ctype_" + dt.Name)
goIdent[name.Name] = name goIdent[name.Name] = name
sub := c.Type(dt.Type, pos) sub := c.Type(dt.Type, pos)
@ -2632,8 +2594,6 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
return t return t
} }
var exactWidthIntegerType = regexp.MustCompile(`^(__)?u?int(8|16|32|64)_t$`)
// isStructUnionClass reports whether the type described by the Go syntax x // isStructUnionClass reports whether the type described by the Go syntax x
// is a struct, union, or class with a tag. // is a struct, union, or class with a tag.
func isStructUnionClass(x ast.Expr) bool { func isStructUnionClass(x ast.Expr) bool {

View file

@ -1367,19 +1367,19 @@ func c(repr string, args ...interface{}) *TypeRepr {
// Map predeclared Go types to Type. // Map predeclared Go types to Type.
var goTypes = map[string]*Type{ var goTypes = map[string]*Type{
"bool": {Size: 1, Align: 1, C: c("uint8_t")}, "bool": {Size: 1, Align: 1, C: c("GoUint8")},
"byte": {Size: 1, Align: 1, C: c("uint8_t")}, "byte": {Size: 1, Align: 1, C: c("GoUint8")},
"int": {Size: 0, Align: 0, C: c("GoInt")}, "int": {Size: 0, Align: 0, C: c("GoInt")},
"uint": {Size: 0, Align: 0, C: c("GoUint")}, "uint": {Size: 0, Align: 0, C: c("GoUint")},
"rune": {Size: 4, Align: 4, C: c("int32_t")}, "rune": {Size: 4, Align: 4, C: c("GoInt32")},
"int8": {Size: 1, Align: 1, C: c("int8_t")}, "int8": {Size: 1, Align: 1, C: c("GoInt8")},
"uint8": {Size: 1, Align: 1, C: c("uint8_t")}, "uint8": {Size: 1, Align: 1, C: c("GoUint8")},
"int16": {Size: 2, Align: 2, C: c("int16_t")}, "int16": {Size: 2, Align: 2, C: c("GoInt16")},
"uint16": {Size: 2, Align: 2, C: c("uint16_t")}, "uint16": {Size: 2, Align: 2, C: c("GoUint16")},
"int32": {Size: 4, Align: 4, C: c("int32_t")}, "int32": {Size: 4, Align: 4, C: c("GoInt32")},
"uint32": {Size: 4, Align: 4, C: c("uint32_t")}, "uint32": {Size: 4, Align: 4, C: c("GoUint32")},
"int64": {Size: 8, Align: 8, C: c("int64_t")}, "int64": {Size: 8, Align: 8, C: c("GoInt64")},
"uint64": {Size: 8, Align: 8, C: c("uint64_t")}, "uint64": {Size: 8, Align: 8, C: c("GoUint64")},
"float32": {Size: 4, Align: 4, C: c("GoFloat32")}, "float32": {Size: 4, Align: 4, C: c("GoFloat32")},
"float64": {Size: 8, Align: 8, C: c("GoFloat64")}, "float64": {Size: 8, Align: 8, C: c("GoFloat64")},
"complex64": {Size: 8, Align: 4, C: c("GoComplex64")}, "complex64": {Size: 8, Align: 4, C: c("GoComplex64")},
@ -1871,10 +1871,16 @@ const gccExportHeaderProlog = `
#ifndef GO_CGO_PROLOGUE_H #ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H #define GO_CGO_PROLOGUE_H
#include <stdint.h> typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef intGOINTBITS_t GoInt; typedef short GoInt16;
typedef uintGOINTBITS_t GoUint; typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoIntGOINTBITS GoInt;
typedef GoUintGOINTBITS GoUint;
typedef __SIZE_TYPE__ GoUintptr; typedef __SIZE_TYPE__ GoUintptr;
typedef float GoFloat32; typedef float GoFloat32;
typedef double GoFloat64; typedef double GoFloat64;

View file

@ -216,11 +216,15 @@ not include a stack overflow check. This is most commonly used by low-level
runtime sources invoked at times when it is unsafe for the calling goroutine to be runtime sources invoked at times when it is unsafe for the calling goroutine to be
preempted. preempted.
//go:linkname localname importpath.name //go:linkname localname [importpath.name]
The //go:linkname directive instructs the compiler to use ``importpath.name'' as the The //go:linkname directive instructs the compiler to use ``importpath.name'' as the
object file symbol name for the variable or function declared as ``localname'' in the object file symbol name for the variable or function declared as ``localname'' in the
source code. Because this directive can subvert the type system and package source code.
If the ``importpath.name'' argument is omitted, the directive uses the
symbol's default object file symbol name and only has the effect of making
the symbol accessible to other packages.
Because this directive can subvert the type system and package
modularity, it is only enabled in files that have imported "unsafe". modularity, it is only enabled in files that have imported "unsafe".
*/ */
package main package main

View file

@ -65,7 +65,6 @@ var knownFormats = map[string]string{
"*math/big.Int %s": "", "*math/big.Int %s": "",
"*math/big.Int %v": "", "*math/big.Int %v": "",
"[16]byte %x": "", "[16]byte %x": "",
"[]*cmd/compile/internal/gc.Node %v": "",
"[]*cmd/compile/internal/ssa.Block %v": "", "[]*cmd/compile/internal/ssa.Block %v": "",
"[]*cmd/compile/internal/ssa.Value %v": "", "[]*cmd/compile/internal/ssa.Value %v": "",
"[][]string %q": "", "[][]string %q": "",
@ -172,36 +171,38 @@ var knownFormats = map[string]string{
"interface{} %s": "", "interface{} %s": "",
"interface{} %v": "", "interface{} %v": "",
"map[*cmd/compile/internal/gc.Node]*cmd/compile/internal/ssa.Value %v": "", "map[*cmd/compile/internal/gc.Node]*cmd/compile/internal/ssa.Value %v": "",
"map[*cmd/compile/internal/gc.Node][]*cmd/compile/internal/gc.Node %v": "",
"map[cmd/compile/internal/ssa.ID]uint32 %v": "", "map[cmd/compile/internal/ssa.ID]uint32 %v": "",
"math/big.Accuracy %s": "", "math/big.Accuracy %s": "",
"reflect.Type %s": "", "reflect.Type %s": "",
"rune %#U": "", "rune %#U": "",
"rune %c": "", "rune %c": "",
"rune %q": "", "rune %q": "",
"string %-*s": "", "string %-*s": "",
"string %-16s": "", "string %-16s": "",
"string %-6s": "", "string %-6s": "",
"string %.*s": "", "string %.*s": "",
"string %q": "", "string %q": "",
"string %s": "", "string %s": "",
"string %v": "", "string %v": "",
"time.Duration %d": "", "time.Duration %d": "",
"time.Duration %v": "", "time.Duration %v": "",
"uint %04x": "", "uint %04x": "",
"uint %5d": "", "uint %5d": "",
"uint %d": "", "uint %d": "",
"uint %x": "", "uint %x": "",
"uint16 %d": "", "uint16 %d": "",
"uint16 %v": "", "uint16 %v": "",
"uint16 %x": "", "uint16 %x": "",
"uint32 %#x": "", "uint32 %#x": "",
"uint32 %d": "", "uint32 %d": "",
"uint32 %v": "", "uint32 %v": "",
"uint32 %x": "", "uint32 %x": "",
"uint64 %08x": "", "uint64 %08x": "",
"uint64 %d": "", "uint64 %d": "",
"uint64 %x": "", "uint64 %x": "",
"uint8 %d": "", "uint8 %d": "",
"uint8 %x": "", "uint8 %v": "",
"uintptr %d": "", "uint8 %x": "",
"uintptr %d": "",
} }

View file

@ -4,6 +4,20 @@ package gc
import "strconv" import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[Pxxx-0]
_ = x[PEXTERN-1]
_ = x[PAUTO-2]
_ = x[PAUTOHEAP-3]
_ = x[PPARAM-4]
_ = x[PPARAMOUT-5]
_ = x[PFUNC-6]
_ = x[PDISCARD-7]
}
const _Class_name = "PxxxPEXTERNPAUTOPAUTOHEAPPPARAMPPARAMOUTPFUNCPDISCARD" const _Class_name = "PxxxPEXTERNPAUTOPAUTOHEAPPPARAMPPARAMOUTPFUNCPDISCARD"
var _Class_index = [...]uint8{0, 4, 11, 16, 25, 31, 40, 45, 53} var _Class_index = [...]uint8{0, 4, 11, 16, 25, 31, 40, 45, 53}

View file

@ -27,12 +27,12 @@ const (
type Val struct { type Val struct {
// U contains one of: // U contains one of:
// bool bool when n.ValCtype() == CTBOOL // bool bool when Ctype() == CTBOOL
// *Mpint int when n.ValCtype() == CTINT, rune when n.ValCtype() == CTRUNE // *Mpint int when Ctype() == CTINT, rune when Ctype() == CTRUNE
// *Mpflt float when n.ValCtype() == CTFLT // *Mpflt float when Ctype() == CTFLT
// *Mpcplx pair of floats when n.ValCtype() == CTCPLX // *Mpcplx pair of floats when Ctype() == CTCPLX
// string string when n.ValCtype() == CTSTR // string string when Ctype() == CTSTR
// *Nilval when n.ValCtype() == CTNIL // *Nilval when Ctype() == CTNIL
U interface{} U interface{}
} }

View file

@ -802,6 +802,7 @@ opSwitch:
case ODEFER: case ODEFER:
if e.loopdepth == 1 { // top level if e.loopdepth == 1 { // top level
n.Esc = EscNever // force stack allocation of defer record (see ssa.go)
break break
} }
// arguments leak out of scope // arguments leak out of scope

View file

@ -882,6 +882,7 @@ func (e *Escape) augmentParamHole(k EscHole, where *Node) EscHole {
// non-transient location to avoid arguments from being // non-transient location to avoid arguments from being
// transiently allocated. // transiently allocated.
if where.Op == ODEFER && e.loopDepth == 1 { if where.Op == ODEFER && e.loopDepth == 1 {
where.Esc = EscNever // force stack allocation of defer record (see ssa.go)
// TODO(mdempsky): Eliminate redundant EscLocation allocs. // TODO(mdempsky): Eliminate redundant EscLocation allocs.
return e.teeHole(k, e.newLoc(nil, false).asHole()) return e.teeHole(k, e.newLoc(nil, false).asHole())
} }

View file

@ -14,17 +14,21 @@ import (
const ( const (
BADWIDTH = types.BADWIDTH BADWIDTH = types.BADWIDTH
)
var (
// maximum size variable which we will allocate on the stack. // maximum size variable which we will allocate on the stack.
// This limit is for explicit variable declarations like "var x T" or "x := ...". // This limit is for explicit variable declarations like "var x T" or "x := ...".
maxStackVarSize = 10 * 1024 * 1024 // Note: the flag smallframes can update this value.
maxStackVarSize = int64(10 * 1024 * 1024)
// maximum size of implicit variables that we will allocate on the stack. // maximum size of implicit variables that we will allocate on the stack.
// p := new(T) allocating T on the stack // p := new(T) allocating T on the stack
// p := &T{} allocating T on the stack // p := &T{} allocating T on the stack
// s := make([]T, n) allocating [n]T on the stack // s := make([]T, n) allocating [n]T on the stack
// s := []byte("...") allocating [n]byte on the stack // s := []byte("...") allocating [n]byte on the stack
maxImplicitStackVarSize = 64 * 1024 // Note: the flag smallframes can update this value.
maxImplicitStackVarSize = int64(64 * 1024)
) )
// isRuntimePkg reports whether p is package runtime. // isRuntimePkg reports whether p is package runtime.
@ -287,6 +291,7 @@ var (
assertI2I, assertI2I,
assertI2I2, assertI2I2,
deferproc, deferproc,
deferprocStack,
Deferreturn, Deferreturn,
Duffcopy, Duffcopy,
Duffzero, Duffzero,

View file

@ -201,7 +201,8 @@ func (f *Func) initLSym(hasBody bool) {
var aliasABI obj.ABI var aliasABI obj.ABI
needABIAlias := false needABIAlias := false
if abi, ok := symabiDefs[f.lsym.Name]; ok && abi == obj.ABI0 { defABI, hasDefABI := symabiDefs[f.lsym.Name]
if hasDefABI && defABI == obj.ABI0 {
// Symbol is defined as ABI0. Create an // Symbol is defined as ABI0. Create an
// Internal -> ABI0 wrapper. // Internal -> ABI0 wrapper.
f.lsym.SetABI(obj.ABI0) f.lsym.SetABI(obj.ABI0)
@ -215,25 +216,24 @@ func (f *Func) initLSym(hasBody bool) {
} }
} }
if abi, ok := symabiRefs[f.lsym.Name]; ok && abi == obj.ABI0 { isLinknameExported := nam.Sym.Linkname != "" && (hasBody || hasDefABI)
// Symbol is referenced as ABI0. Create an if abi, ok := symabiRefs[f.lsym.Name]; (ok && abi == obj.ABI0) || isLinknameExported {
// ABI0 -> Internal wrapper if necessary. // Either 1) this symbol is definitely
// referenced as ABI0 from this package; or 2)
// this symbol is defined in this package but
// given a linkname, indicating that it may be
// referenced from another package. Create an
// ABI0 -> Internal wrapper so it can be
// called as ABI0. In case 2, it's important
// that we know it's defined in this package
// since other packages may "pull" symbols
// using linkname and we don't want to create
// duplicate ABI wrappers.
if f.lsym.ABI() != obj.ABI0 { if f.lsym.ABI() != obj.ABI0 {
needABIAlias, aliasABI = true, obj.ABI0 needABIAlias, aliasABI = true, obj.ABI0
} }
} }
if !needABIAlias && allABIs {
// The compiler was asked to produce ABI
// wrappers for everything.
switch f.lsym.ABI() {
case obj.ABI0:
needABIAlias, aliasABI = true, obj.ABIInternal
case obj.ABIInternal:
needABIAlias, aliasABI = true, obj.ABI0
}
}
if needABIAlias { if needABIAlias {
// These LSyms have the same name as the // These LSyms have the same name as the
// native function, so we create them directly // native function, so we create them directly

View file

@ -31,7 +31,7 @@ func renameinit() *types.Sym {
// 2) Initialize all the variables that have initializers. // 2) Initialize all the variables that have initializers.
// 3) Run any init functions. // 3) Run any init functions.
func fninit(n []*Node) { func fninit(n []*Node) {
nf := initfix(n) nf := initOrder(n)
var deps []*obj.LSym // initTask records for packages the current package depends on var deps []*obj.LSym // initTask records for packages the current package depends on
var fns []*obj.LSym // functions to call for package initialization var fns []*obj.LSym // functions to call for package initialization

View file

@ -0,0 +1,355 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gc
import (
"bytes"
"container/heap"
"fmt"
)
// Package initialization
//
// Here we implement the algorithm for ordering package-level variable
// initialization. The spec is written in terms of variable
// initialization, but multiple variables initialized by a single
// assignment are handled together, so here we instead focus on
// ordering initialization assignments. Conveniently, this maps well
// to how we represent package-level initializations using the Node
// AST.
//
// Assignments are in one of three phases: NotStarted, Pending, or
// Done. For assignments in the Pending phase, we use Xoffset to
// record the number of unique variable dependencies whose
// initialization assignment is not yet Done. We also maintain a
// "blocking" map that maps assignments back to all of the assignments
// that depend on it.
//
// For example, for an initialization like:
//
// var x = f(a, b, b)
// var a, b = g()
//
// the "x = f(a, b, b)" assignment depends on two variables (a and b),
// so its Xoffset will be 2. Correspondingly, the "a, b = g()"
// assignment's "blocking" entry will have two entries back to x's
// assignment.
//
// Logically, initialization works by (1) taking all NotStarted
// assignments, calculating their dependencies, and marking them
// Pending; (2) adding all Pending assignments with Xoffset==0 to a
// "ready" priority queue (ordered by variable declaration position);
// and (3) iteratively processing the next Pending assignment from the
// queue, decreasing the Xoffset of assignments it's blocking, and
// adding them to the queue if decremented to 0.
//
// As an optimization, we actually apply each of these three steps for
// each assignment. This yields the same order, but keeps queue size
// down and thus also heap operation costs.
// Static initialization phase.
// These values are stored in two bits in Node.flags.
const (
InitNotStarted = iota
InitDone
InitPending
)
type InitOrder struct {
// blocking maps initialization assignments to the assignments
// that depend on it.
blocking map[*Node][]*Node
// ready is the queue of Pending initialization assignments
// that are ready for initialization.
ready declOrder
}
// initOrder computes initialization order for a list l of
// package-level declarations (in declaration order) and outputs the
// corresponding list of statements to include in the init() function
// body.
func initOrder(l []*Node) []*Node {
s := InitSchedule{
initplans: make(map[*Node]*InitPlan),
inittemps: make(map[*Node]*Node),
}
o := InitOrder{
blocking: make(map[*Node][]*Node),
}
// Process all package-level assignment in declaration order.
for _, n := range l {
switch n.Op {
case OAS, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
o.processAssign(n)
o.flushReady(s.staticInit)
case ODCLCONST, ODCLFUNC, ODCLTYPE:
// nop
default:
Fatalf("unexpected package-level statement: %v", n)
}
}
// Check that all assignments are now Done; if not, there must
// have been a dependency cycle.
for _, n := range l {
switch n.Op {
case OAS, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
if n.Initorder() != InitDone {
// If there have already been errors
// printed, those errors may have
// confused us and there might not be
// a loop. Let the user fix those
// first.
if nerrors > 0 {
errorexit()
}
findInitLoopAndExit(firstLHS(n), new([]*Node))
Fatalf("initialization unfinished, but failed to identify loop")
}
}
}
// Invariant consistency check. If this is non-zero, then we
// should have found a cycle above.
if len(o.blocking) != 0 {
Fatalf("expected empty map: %v", o.blocking)
}
return s.out
}
func (o *InitOrder) processAssign(n *Node) {
if n.Initorder() != InitNotStarted || n.Xoffset != BADWIDTH {
Fatalf("unexpected state: %v, %v, %v", n, n.Initorder(), n.Xoffset)
}
n.SetInitorder(InitPending)
n.Xoffset = 0
// Compute number of variable dependencies and build the
// inverse dependency ("blocking") graph.
for dep := range collectDeps(n, true) {
defn := dep.Name.Defn
// Skip dependencies on functions (PFUNC) and
// variables already initialized (InitDone).
if dep.Class() != PEXTERN || defn.Initorder() == InitDone {
continue
}
n.Xoffset++
o.blocking[defn] = append(o.blocking[defn], n)
}
if n.Xoffset == 0 {
heap.Push(&o.ready, n)
}
}
// flushReady repeatedly applies initialize to the earliest (in
// declaration order) assignment ready for initialization and updates
// the inverse dependency ("blocking") graph.
func (o *InitOrder) flushReady(initialize func(*Node)) {
for o.ready.Len() != 0 {
n := heap.Pop(&o.ready).(*Node)
if n.Initorder() != InitPending || n.Xoffset != 0 {
Fatalf("unexpected state: %v, %v, %v", n, n.Initorder(), n.Xoffset)
}
initialize(n)
n.SetInitorder(InitDone)
n.Xoffset = BADWIDTH
blocked := o.blocking[n]
delete(o.blocking, n)
for _, m := range blocked {
m.Xoffset--
if m.Xoffset == 0 {
heap.Push(&o.ready, m)
}
}
}
}
// findInitLoopAndExit searches for an initialization loop involving variable
// or function n. If one is found, it reports the loop as an error and exits.
//
// path points to a slice used for tracking the sequence of
// variables/functions visited. Using a pointer to a slice allows the
// slice capacity to grow and limit reallocations.
func findInitLoopAndExit(n *Node, path *[]*Node) {
// We implement a simple DFS loop-finding algorithm. This
// could be faster, but initialization cycles are rare.
for i, x := range *path {
if x == n {
reportInitLoopAndExit((*path)[i:])
return
}
}
// There might be multiple loops involving n; by sorting
// references, we deterministically pick the one reported.
refers := collectDeps(n.Name.Defn, false).Sorted(func(ni, nj *Node) bool {
return ni.Pos.Before(nj.Pos)
})
*path = append(*path, n)
for _, ref := range refers {
// Short-circuit variables that were initialized.
if ref.Class() == PEXTERN && ref.Name.Defn.Initorder() == InitDone {
continue
}
findInitLoopAndExit(ref, path)
}
*path = (*path)[:len(*path)-1]
}
// reportInitLoopAndExit reports and initialization loop as an error
// and exits. However, if l is not actually an initialization loop, it
// simply returns instead.
func reportInitLoopAndExit(l []*Node) {
// Rotate loop so that the earliest variable declaration is at
// the start.
i := -1
for j, n := range l {
if n.Class() == PEXTERN && (i == -1 || n.Pos.Before(l[i].Pos)) {
i = j
}
}
if i == -1 {
// False positive: loop only involves recursive
// functions. Return so that findInitLoop can continue
// searching.
return
}
l = append(l[i:], l[:i]...)
// TODO(mdempsky): Method values are printed as "T.m-fm"
// rather than "T.m". Figure out how to avoid that.
var msg bytes.Buffer
fmt.Fprintf(&msg, "initialization loop:\n")
for _, n := range l {
fmt.Fprintf(&msg, "\t%v: %v refers to\n", n.Line(), n)
}
fmt.Fprintf(&msg, "\t%v: %v", l[0].Line(), l[0])
yyerrorl(l[0].Pos, msg.String())
errorexit()
}
// collectDeps returns all of the package-level functions and
// variables that declaration n depends on. If transitive is true,
// then it also includes the transitive dependencies of any depended
// upon functions (but not variables).
func collectDeps(n *Node, transitive bool) NodeSet {
d := initDeps{transitive: transitive}
switch n.Op {
case OAS:
d.inspect(n.Right)
case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:
d.inspect(n.Rlist.First())
case ODCLFUNC:
d.inspectList(n.Nbody)
default:
Fatalf("unexpected Op: %v", n.Op)
}
return d.seen
}
type initDeps struct {
transitive bool
seen NodeSet
}
func (d *initDeps) inspect(n *Node) { inspect(n, d.visit) }
func (d *initDeps) inspectList(l Nodes) { inspectList(l, d.visit) }
// visit calls foundDep on any package-level functions or variables
// referenced by n, if any.
func (d *initDeps) visit(n *Node) bool {
switch n.Op {
case ONAME:
if n.isMethodExpression() {
d.foundDep(asNode(n.Type.FuncType().Nname))
return false
}
switch n.Class() {
case PEXTERN, PFUNC:
d.foundDep(n)
}
case OCLOSURE:
d.inspectList(n.Func.Closure.Nbody)
case ODOTMETH, OCALLPART:
d.foundDep(asNode(n.Type.FuncType().Nname))
}
return true
}
// foundDep records that we've found a dependency on n by adding it to
// seen.
func (d *initDeps) foundDep(n *Node) {
// Can happen with method expressions involving interface
// types; e.g., fixedbugs/issue4495.go.
if n == nil {
return
}
// Names without definitions aren't interesting as far as
// initialization ordering goes.
if n.Name.Defn == nil {
return
}
if d.seen.Has(n) {
return
}
d.seen.Add(n)
if d.transitive && n.Class() == PFUNC {
d.inspectList(n.Name.Defn.Nbody)
}
}
// declOrder implements heap.Interface, ordering assignment statements
// by the position of their first LHS expression.
//
// N.B., the Pos of the first LHS expression is used because because
// an OAS node's Pos may not be unique. For example, given the
// declaration "var a, b = f(), g()", "a" must be ordered before "b",
// but both OAS nodes use the "=" token's position as their Pos.
type declOrder []*Node
func (s declOrder) Len() int { return len(s) }
func (s declOrder) Less(i, j int) bool { return firstLHS(s[i]).Pos.Before(firstLHS(s[j]).Pos) }
func (s declOrder) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s *declOrder) Push(x interface{}) { *s = append(*s, x.(*Node)) }
func (s *declOrder) Pop() interface{} {
n := (*s)[len(*s)-1]
*s = (*s)[:len(*s)-1]
return n
}
// firstLHS returns the first expression on the left-hand side of
// assignment n.
func firstLHS(n *Node) *Node {
switch n.Op {
case OAS:
return n.Left
case OAS2DOTTYPE, OAS2FUNC, OAS2RECV, OAS2MAPR:
return n.List.First()
}
Fatalf("unexpected Op: %v", n.Op)
return nil
}

View file

@ -190,6 +190,10 @@ func Main(archInit func(*Arch)) {
Nacl = objabi.GOOS == "nacl" Nacl = objabi.GOOS == "nacl"
Wasm := objabi.GOARCH == "wasm" Wasm := objabi.GOARCH == "wasm"
// Whether the limit for stack-allocated objects is much smaller than normal.
// This can be helpful for diagnosing certain causes of GC latency. See #27732.
smallFrames := false
flag.BoolVar(&compiling_runtime, "+", false, "compiling runtime") flag.BoolVar(&compiling_runtime, "+", false, "compiling runtime")
flag.BoolVar(&compiling_std, "std", false, "compiling standard library") flag.BoolVar(&compiling_std, "std", false, "compiling standard library")
objabi.Flagcount("%", "debug non-static initializers", &Debug['%']) objabi.Flagcount("%", "debug non-static initializers", &Debug['%'])
@ -255,19 +259,24 @@ func Main(archInit func(*Arch)) {
flag.StringVar(&goversion, "goversion", "", "required version of the runtime") flag.StringVar(&goversion, "goversion", "", "required version of the runtime")
var symabisPath string var symabisPath string
flag.StringVar(&symabisPath, "symabis", "", "read symbol ABIs from `file`") flag.StringVar(&symabisPath, "symabis", "", "read symbol ABIs from `file`")
flag.BoolVar(&allABIs, "allabis", false, "generate ABI wrappers for all symbols (for bootstrap)")
flag.StringVar(&traceprofile, "traceprofile", "", "write an execution trace to `file`") flag.StringVar(&traceprofile, "traceprofile", "", "write an execution trace to `file`")
flag.StringVar(&blockprofile, "blockprofile", "", "write block profile to `file`") flag.StringVar(&blockprofile, "blockprofile", "", "write block profile to `file`")
flag.StringVar(&mutexprofile, "mutexprofile", "", "write mutex profile to `file`") flag.StringVar(&mutexprofile, "mutexprofile", "", "write mutex profile to `file`")
flag.StringVar(&benchfile, "bench", "", "append benchmark times to `file`") flag.StringVar(&benchfile, "bench", "", "append benchmark times to `file`")
flag.BoolVar(&newescape, "newescape", true, "enable new escape analysis") flag.BoolVar(&newescape, "newescape", true, "enable new escape analysis")
flag.BoolVar(&smallFrames, "smallframes", false, "reduce the size limit for stack allocated objects")
flag.BoolVar(&Ctxt.UseBASEntries, "dwarfbasentries", Ctxt.UseBASEntries, "use base address selection entries in DWARF") flag.BoolVar(&Ctxt.UseBASEntries, "dwarfbasentries", Ctxt.UseBASEntries, "use base address selection entries in DWARF")
objabi.Flagparse(usage) objabi.Flagparse(usage)
// Record flags that affect the build result. (And don't // Record flags that affect the build result. (And don't
// record flags that don't, since that would cause spurious // record flags that don't, since that would cause spurious
// changes in the binary.) // changes in the binary.)
recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "newescape", "dwarfbasentries") recordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarflocationlists", "newescape", "dwarfbasentries", "smallframes")
if smallFrames {
maxStackVarSize = 128 * 1024
maxImplicitStackVarSize = 16 * 1024
}
Ctxt.Flag_shared = flag_dynlink || flag_shared Ctxt.Flag_shared = flag_dynlink || flag_shared
Ctxt.Flag_dynlink = flag_dynlink Ctxt.Flag_dynlink = flag_dynlink
@ -839,11 +848,6 @@ func readImportCfg(file string) {
// name, where the local package prefix is always `"".` // name, where the local package prefix is always `"".`
var symabiDefs, symabiRefs map[string]obj.ABI var symabiDefs, symabiRefs map[string]obj.ABI
// allABIs indicates that all symbol definitions should have ABI
// wrappers. This is used during toolchain bootstrapping to avoid
// having to find cross-package references.
var allABIs bool
// readSymABIs reads a symabis file that specifies definitions and // readSymABIs reads a symabis file that specifies definitions and
// references of text symbols by ABI. // references of text symbols by ABI.
// //

View file

@ -244,10 +244,21 @@ func (p *noder) node() {
xtop = append(xtop, p.decls(p.file.DeclList)...) xtop = append(xtop, p.decls(p.file.DeclList)...)
for _, n := range p.linknames { for _, n := range p.linknames {
if imported_unsafe { if !imported_unsafe {
lookup(n.local).Linkname = n.remote
} else {
p.yyerrorpos(n.pos, "//go:linkname only allowed in Go files that import \"unsafe\"") p.yyerrorpos(n.pos, "//go:linkname only allowed in Go files that import \"unsafe\"")
continue
}
s := lookup(n.local)
if n.remote != "" {
s.Linkname = n.remote
} else {
// Use the default object symbol name if the
// user didn't provide one.
if myimportpath == "" {
p.yyerrorpos(n.pos, "//go:linkname requires linkname argument or -p compiler flag")
} else {
s.Linkname = objabi.PathToPrefix(myimportpath) + "." + n.local
}
} }
} }
@ -1476,11 +1487,20 @@ func (p *noder) pragma(pos syntax.Pos, text string) syntax.Pragma {
case strings.HasPrefix(text, "go:linkname "): case strings.HasPrefix(text, "go:linkname "):
f := strings.Fields(text) f := strings.Fields(text)
if len(f) != 3 { if !(2 <= len(f) && len(f) <= 3) {
p.error(syntax.Error{Pos: pos, Msg: "usage: //go:linkname localname linkname"}) p.error(syntax.Error{Pos: pos, Msg: "usage: //go:linkname localname [linkname]"})
break break
} }
p.linknames = append(p.linknames, linkname{pos, f[1], f[2]}) // The second argument is optional. If omitted, we use
// the default object symbol name for this and
// linkname only serves to mark this symbol as
// something that may be referenced via the object
// symbol name from another package.
var target string
if len(f) == 3 {
target = f[2]
}
p.linknames = append(p.linknames, linkname{pos, f[1], target})
case strings.HasPrefix(text, "go:cgo_import_dynamic "): case strings.HasPrefix(text, "go:cgo_import_dynamic "):
// This is permitted for general use because Solaris // This is permitted for general use because Solaris

View file

@ -30,10 +30,10 @@ func _() {
_ = x[OSTR2RUNES-19] _ = x[OSTR2RUNES-19]
_ = x[OAS-20] _ = x[OAS-20]
_ = x[OAS2-21] _ = x[OAS2-21]
_ = x[OAS2FUNC-22] _ = x[OAS2DOTTYPE-22]
_ = x[OAS2RECV-23] _ = x[OAS2FUNC-23]
_ = x[OAS2MAPR-24] _ = x[OAS2MAPR-24]
_ = x[OAS2DOTTYPE-25] _ = x[OAS2RECV-25]
_ = x[OASOP-26] _ = x[OASOP-26]
_ = x[OCALL-27] _ = x[OCALL-27]
_ = x[OCALLFUNC-28] _ = x[OCALLFUNC-28]
@ -164,9 +164,9 @@ func _() {
_ = x[OEND-153] _ = x[OEND-153]
} }
const _Op_name = "XXXNAMENONAMETYPEPACKLITERALADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESASAS2AS2FUNCAS2RECVAS2MAPRAS2DOTTYPEASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLFIELDDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMULDIVMODLSHRSHANDANDNOTNEWNEWOBJNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECVRUNESTRSELRECVSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFBLOCKBREAKCASEXCASECONTINUEDEFEREMPTYFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWTCHANTMAPTSTRUCTTINTERTFUNCTARRAYDDDDDDARGINLCALLEFACEITABIDATASPTRCLOSUREVARCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKRETJMPGETGEND" const _Op_name = "XXXNAMENONAMETYPEPACKLITERALADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLFIELDDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMULDIVMODLSHRSHANDANDNOTNEWNEWOBJNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECVRUNESTRSELRECVSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFBLOCKBREAKCASEXCASECONTINUEDEFEREMPTYFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWTCHANTMAPTSTRUCTTINTERTFUNCTARRAYDDDDDDARGINLCALLEFACEITABIDATASPTRCLOSUREVARCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKRETJMPGETGEND"
var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 36, 39, 45, 49, 55, 61, 70, 82, 91, 100, 112, 121, 123, 126, 133, 140, 147, 157, 161, 165, 173, 181, 190, 198, 201, 206, 213, 220, 226, 235, 243, 251, 257, 261, 270, 277, 281, 284, 291, 299, 307, 314, 320, 323, 329, 336, 344, 348, 355, 363, 365, 367, 369, 371, 373, 375, 380, 385, 393, 396, 405, 408, 412, 420, 427, 436, 439, 442, 445, 448, 451, 454, 460, 463, 469, 472, 478, 482, 485, 489, 494, 499, 505, 510, 514, 519, 527, 535, 541, 550, 561, 568, 572, 579, 586, 594, 598, 602, 606, 613, 620, 628, 634, 639, 644, 648, 653, 661, 666, 671, 675, 678, 686, 690, 692, 697, 699, 704, 710, 716, 722, 728, 733, 737, 744, 750, 755, 761, 764, 770, 777, 782, 786, 791, 795, 805, 810, 818, 824, 831, 838, 844, 851, 857, 861, 864} var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 36, 39, 45, 49, 55, 61, 70, 82, 91, 100, 112, 121, 123, 126, 136, 143, 150, 157, 161, 165, 173, 181, 190, 198, 201, 206, 213, 220, 226, 235, 243, 251, 257, 261, 270, 277, 281, 284, 291, 299, 307, 314, 320, 323, 329, 336, 344, 348, 355, 363, 365, 367, 369, 371, 373, 375, 380, 385, 393, 396, 405, 408, 412, 420, 427, 436, 439, 442, 445, 448, 451, 454, 460, 463, 469, 472, 478, 482, 485, 489, 494, 499, 505, 510, 514, 519, 527, 535, 541, 550, 561, 568, 572, 579, 586, 594, 598, 602, 606, 613, 620, 628, 634, 639, 644, 648, 653, 661, 666, 671, 675, 678, 686, 690, 692, 697, 699, 704, 710, 716, 722, 728, 733, 737, 744, 750, 755, 761, 764, 770, 777, 782, 786, 791, 795, 805, 810, 818, 824, 831, 838, 844, 851, 857, 861, 864}
func (i Op) String() string { func (i Op) String() string {
if i >= Op(len(_Op_index)-1) { if i >= Op(len(_Op_index)-1) {

View file

@ -317,6 +317,48 @@ func hiter(t *types.Type) *types.Type {
return hiter return hiter
} }
// deferstruct makes a runtime._defer structure, with additional space for
// stksize bytes of args.
func deferstruct(stksize int64) *types.Type {
makefield := func(name string, typ *types.Type) *types.Field {
f := types.NewField()
f.Type = typ
// Unlike the global makefield function, this one needs to set Pkg
// because these types might be compared (in SSA CSE sorting).
// TODO: unify this makefield and the global one above.
f.Sym = &types.Sym{Name: name, Pkg: localpkg}
return f
}
argtype := types.NewArray(types.Types[TUINT8], stksize)
argtype.SetNoalg(true)
argtype.Width = stksize
argtype.Align = 1
// These fields must match the ones in runtime/runtime2.go:_defer and
// cmd/compile/internal/gc/ssa.go:(*state).call.
fields := []*types.Field{
makefield("siz", types.Types[TUINT32]),
makefield("started", types.Types[TBOOL]),
makefield("heap", types.Types[TBOOL]),
makefield("sp", types.Types[TUINTPTR]),
makefield("pc", types.Types[TUINTPTR]),
// Note: the types here don't really matter. Defer structures
// are always scanned explicitly during stack copying and GC,
// so we make them uintptr type even though they are real pointers.
makefield("fn", types.Types[TUINTPTR]),
makefield("_panic", types.Types[TUINTPTR]),
makefield("link", types.Types[TUINTPTR]),
makefield("args", argtype),
}
// build struct holding the above fields
s := types.New(TSTRUCT)
s.SetNoalg(true)
s.SetFields(fields)
s.Width = widstruct(s, s, 0, 1)
s.Align = uint8(Widthptr)
return s
}
// f is method type, with receiver. // f is method type, with receiver.
// return function type, receiver as first argument (or not). // return function type, receiver as first argument (or not).
func methodfunc(f *types.Type, receiver *types.Type) *types.Type { func methodfunc(f *types.Type, receiver *types.Type) *types.Type {

View file

@ -9,14 +9,6 @@ import (
"fmt" "fmt"
) )
// Static initialization ordering state.
// These values are stored in two bits in Node.flags.
const (
InitNotStarted = iota
InitDone
InitPending
)
type InitEntry struct { type InitEntry struct {
Xoffset int64 // struct, array only Xoffset int64 // struct, array only
Expr *Node // bytes of run-time computed expressions Expr *Node // bytes of run-time computed expressions
@ -26,9 +18,15 @@ type InitPlan struct {
E []InitEntry E []InitEntry
} }
// An InitSchedule is used to decompose assignment statements into
// static and dynamic initialization parts. Static initializations are
// handled by populating variables' linker symbol data, while dynamic
// initializations are accumulated to be executed in order.
type InitSchedule struct { type InitSchedule struct {
out []*Node // out is the ordered list of dynamic initialization
initlist []*Node // statements.
out []*Node
initplans map[*Node]*InitPlan initplans map[*Node]*InitPlan
inittemps map[*Node]*Node inittemps map[*Node]*Node
} }
@ -37,239 +35,33 @@ func (s *InitSchedule) append(n *Node) {
s.out = append(s.out, n) s.out = append(s.out, n)
} }
// init1 walks the AST starting at n, and accumulates in out // staticInit adds an initialization statement n to the schedule.
// the list of definitions needing init code in dependency order. func (s *InitSchedule) staticInit(n *Node) {
func (s *InitSchedule) init1(n *Node) { if !s.tryStaticInit(n) {
if n == nil { if Debug['%'] != 0 {
return Dump("nonstatic", n)
}
s.init1(n.Left)
s.init1(n.Right)
for _, n1 := range n.List.Slice() {
s.init1(n1)
}
if n.isMethodExpression() {
// Methods called as Type.Method(receiver, ...).
// Definitions for method expressions are stored in type->nname.
s.init1(asNode(n.Type.FuncType().Nname))
}
if n.Op != ONAME {
return
}
switch n.Class() {
case PEXTERN, PFUNC:
default:
if n.isBlank() && n.Name.Curfn == nil && n.Name.Defn != nil && n.Name.Defn.Initorder() == InitNotStarted {
// blank names initialization is part of init() but not
// when they are inside a function.
break
} }
return s.append(n)
}
if n.Initorder() == InitDone {
return
}
if n.Initorder() == InitPending {
// Since mutually recursive sets of functions are allowed,
// we don't necessarily raise an error if n depends on a node
// which is already waiting for its dependencies to be visited.
//
// initlist contains a cycle of identifiers referring to each other.
// If this cycle contains a variable, then this variable refers to itself.
// Conversely, if there exists an initialization cycle involving
// a variable in the program, the tree walk will reach a cycle
// involving that variable.
if n.Class() != PFUNC {
s.foundinitloop(n, n)
}
for i := len(s.initlist) - 1; i >= 0; i-- {
x := s.initlist[i]
if x == n {
break
}
if x.Class() != PFUNC {
s.foundinitloop(n, x)
}
}
// The loop involves only functions, ok.
return
}
// reached a new unvisited node.
n.SetInitorder(InitPending)
s.initlist = append(s.initlist, n)
// make sure that everything n depends on is initialized.
// n->defn is an assignment to n
if defn := n.Name.Defn; defn != nil {
switch defn.Op {
default:
Dump("defn", defn)
Fatalf("init1: bad defn")
case ODCLFUNC:
s.init2list(defn.Nbody)
case OAS:
if defn.Left != n {
Dump("defn", defn)
Fatalf("init1: bad defn")
}
if defn.Left.isBlank() && candiscard(defn.Right) {
defn.Op = OEMPTY
defn.Left = nil
defn.Right = nil
break
}
s.init2(defn.Right)
if Debug['j'] != 0 {
fmt.Printf("%v\n", n.Sym)
}
if n.isBlank() || !s.staticinit(n) {
if Debug['%'] != 0 {
Dump("nonstatic", defn)
}
s.append(defn)
}
case OAS2FUNC, OAS2MAPR, OAS2DOTTYPE, OAS2RECV:
if defn.Initorder() == InitDone {
break
}
defn.SetInitorder(InitPending)
for _, n2 := range defn.Rlist.Slice() {
s.init1(n2)
}
if Debug['%'] != 0 {
Dump("nonstatic", defn)
}
s.append(defn)
defn.SetInitorder(InitDone)
}
}
last := len(s.initlist) - 1
if s.initlist[last] != n {
Fatalf("bad initlist %v", s.initlist)
}
s.initlist[last] = nil // allow GC
s.initlist = s.initlist[:last]
n.SetInitorder(InitDone)
}
// foundinitloop prints an init loop error and exits.
func (s *InitSchedule) foundinitloop(node, visited *Node) {
// If there have already been errors printed,
// those errors probably confused us and
// there might not be a loop. Let the user
// fix those first.
flusherrors()
if nerrors > 0 {
errorexit()
}
// Find the index of node and visited in the initlist.
var nodeindex, visitedindex int
for ; s.initlist[nodeindex] != node; nodeindex++ {
}
for ; s.initlist[visitedindex] != visited; visitedindex++ {
}
// There is a loop involving visited. We know about node and
// initlist = n1 <- ... <- visited <- ... <- node <- ...
fmt.Printf("%v: initialization loop:\n", visited.Line())
// Print visited -> ... -> n1 -> node.
for _, n := range s.initlist[visitedindex:] {
fmt.Printf("\t%v %v refers to\n", n.Line(), n.Sym)
}
// Print node -> ... -> visited.
for _, n := range s.initlist[nodeindex:visitedindex] {
fmt.Printf("\t%v %v refers to\n", n.Line(), n.Sym)
}
fmt.Printf("\t%v %v\n", visited.Line(), visited.Sym)
errorexit()
}
// recurse over n, doing init1 everywhere.
func (s *InitSchedule) init2(n *Node) {
if n == nil || n.Initorder() == InitDone {
return
}
if n.Op == ONAME && n.Ninit.Len() != 0 {
Fatalf("name %v with ninit: %+v\n", n.Sym, n)
}
s.init1(n)
s.init2(n.Left)
s.init2(n.Right)
s.init2list(n.Ninit)
s.init2list(n.List)
s.init2list(n.Rlist)
s.init2list(n.Nbody)
switch n.Op {
case OCLOSURE:
s.init2list(n.Func.Closure.Nbody)
case ODOTMETH, OCALLPART:
s.init2(asNode(n.Type.FuncType().Nname))
} }
} }
func (s *InitSchedule) init2list(l Nodes) { // tryStaticInit attempts to statically execute an initialization
for _, n := range l.Slice() { // statement and reports whether it succeeded.
s.init2(n) func (s *InitSchedule) tryStaticInit(n *Node) bool {
// Only worry about simple "l = r" assignments. Multiple
// variable/expression OAS2 assignments have already been
// replaced by multiple simple OAS assignments, and the other
// OAS2* assignments mostly necessitate dynamic execution
// anyway.
if n.Op != OAS {
return false
} }
} if n.Left.isBlank() && candiscard(n.Right) {
return true
func (s *InitSchedule) initreorder(l []*Node) {
for _, n := range l {
switch n.Op {
case ODCLFUNC, ODCLCONST, ODCLTYPE:
continue
}
s.initreorder(n.Ninit.Slice())
n.Ninit.Set(nil)
s.init1(n)
} }
} lno := setlineno(n)
defer func() { lineno = lno }()
// initfix computes initialization order for a list l of top-level return s.staticassign(n.Left, n.Right)
// declarations and outputs the corresponding list of statements
// to include in the init() function body.
func initfix(l []*Node) []*Node {
s := InitSchedule{
initplans: make(map[*Node]*InitPlan),
inittemps: make(map[*Node]*Node),
}
lno := lineno
s.initreorder(l)
lineno = lno
return s.out
}
// compilation of top-level (static) assignments
// into DATA statements if at all possible.
func (s *InitSchedule) staticinit(n *Node) bool {
if n.Op != ONAME || n.Class() != PEXTERN || n.Name.Defn == nil || n.Name.Defn.Op != OAS {
Fatalf("staticinit")
}
lineno = n.Pos
l := n.Name.Defn.Left
r := n.Name.Defn.Right
return s.staticassign(l, r)
} }
// like staticassign but we are copying an already // like staticassign but we are copying an already

View file

@ -68,6 +68,7 @@ func initssaconfig() {
assertI2I = sysfunc("assertI2I") assertI2I = sysfunc("assertI2I")
assertI2I2 = sysfunc("assertI2I2") assertI2I2 = sysfunc("assertI2I2")
deferproc = sysfunc("deferproc") deferproc = sysfunc("deferproc")
deferprocStack = sysfunc("deferprocStack")
Deferreturn = sysfunc("deferreturn") Deferreturn = sysfunc("deferreturn")
Duffcopy = sysvar("duffcopy") // asm func with special ABI Duffcopy = sysvar("duffcopy") // asm func with special ABI
Duffzero = sysvar("duffzero") // asm func with special ABI Duffzero = sysvar("duffzero") // asm func with special ABI
@ -864,7 +865,11 @@ func (s *state) stmt(n *Node) {
} }
} }
case ODEFER: case ODEFER:
s.call(n.Left, callDefer) d := callDefer
if n.Esc == EscNever {
d = callDeferStack
}
s.call(n.Left, d)
case OGO: case OGO:
s.call(n.Left, callGo) s.call(n.Left, callGo)
@ -2859,6 +2864,7 @@ type callKind int8
const ( const (
callNormal callKind = iota callNormal callKind = iota
callDefer callDefer
callDeferStack
callGo callGo
) )
@ -3093,7 +3099,7 @@ func init() {
s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v) s.vars[&memVar] = s.newValue1(ssa.OpSelect1, types.TypeMem, v)
return s.newValue1(ssa.OpSelect0, types.Types[TUINT32], v) return s.newValue1(ssa.OpSelect0, types.Types[TUINT32], v)
}, },
sys.PPC64) sys.PPC64, sys.S390X)
addF("runtime/internal/atomic", "Loadp", addF("runtime/internal/atomic", "Loadp",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value { func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
v := s.newValue2(ssa.OpAtomicLoadPtr, types.NewTuple(s.f.Config.Types.BytePtr, types.TypeMem), args[0], s.mem()) v := s.newValue2(ssa.OpAtomicLoadPtr, types.NewTuple(s.f.Config.Types.BytePtr, types.TypeMem), args[0], s.mem())
@ -3125,7 +3131,7 @@ func init() {
s.vars[&memVar] = s.newValue3(ssa.OpAtomicStoreRel32, types.TypeMem, args[0], args[1], s.mem()) s.vars[&memVar] = s.newValue3(ssa.OpAtomicStoreRel32, types.TypeMem, args[0], args[1], s.mem())
return nil return nil
}, },
sys.PPC64) sys.PPC64, sys.S390X)
addF("runtime/internal/atomic", "Xchg", addF("runtime/internal/atomic", "Xchg",
func(s *state, n *Node, args []*ssa.Value) *ssa.Value { func(s *state, n *Node, args []*ssa.Value) *ssa.Value {
@ -3658,9 +3664,6 @@ func init() {
// findIntrinsic returns a function which builds the SSA equivalent of the // findIntrinsic returns a function which builds the SSA equivalent of the
// function identified by the symbol sym. If sym is not an intrinsic call, returns nil. // function identified by the symbol sym. If sym is not an intrinsic call, returns nil.
func findIntrinsic(sym *types.Sym) intrinsicBuilder { func findIntrinsic(sym *types.Sym) intrinsicBuilder {
if ssa.IntrinsicsDisable {
return nil
}
if sym == nil || sym.Pkg == nil { if sym == nil || sym.Pkg == nil {
return nil return nil
} }
@ -3680,6 +3683,13 @@ func findIntrinsic(sym *types.Sym) intrinsicBuilder {
} }
fn := sym.Name fn := sym.Name
if ssa.IntrinsicsDisable {
if pkg == "runtime" && (fn == "getcallerpc" || fn == "getcallersp" || fn == "getclosureptr") {
// These runtime functions don't have definitions, must be intrinsics.
} else {
return nil
}
}
return intrinsics[intrinsicKey{thearch.LinkArch.Arch, pkg, fn}] return intrinsics[intrinsicKey{thearch.LinkArch.Arch, pkg, fn}]
} }
@ -3795,74 +3805,132 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
rcvr = s.newValue1(ssa.OpIData, types.Types[TUINTPTR], i) rcvr = s.newValue1(ssa.OpIData, types.Types[TUINTPTR], i)
} }
dowidth(fn.Type) dowidth(fn.Type)
stksize := fn.Type.ArgWidth() // includes receiver stksize := fn.Type.ArgWidth() // includes receiver, args, and results
// Run all assignments of temps. // Run all assignments of temps.
// The temps are introduced to avoid overwriting argument // The temps are introduced to avoid overwriting argument
// slots when arguments themselves require function calls. // slots when arguments themselves require function calls.
s.stmtList(n.List) s.stmtList(n.List)
// Store arguments to stack, including defer/go arguments and receiver for method calls.
// These are written in SP-offset order.
argStart := Ctxt.FixedFrameSize()
// Defer/go args.
if k != callNormal {
// Write argsize and closure (args to newproc/deferproc).
argsize := s.constInt32(types.Types[TUINT32], int32(stksize))
addr := s.constOffPtrSP(s.f.Config.Types.UInt32Ptr, argStart)
s.store(types.Types[TUINT32], addr, argsize)
addr = s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart+int64(Widthptr))
s.store(types.Types[TUINTPTR], addr, closure)
stksize += 2 * int64(Widthptr)
argStart += 2 * int64(Widthptr)
}
// Set receiver (for interface calls).
if rcvr != nil {
addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart)
s.store(types.Types[TUINTPTR], addr, rcvr)
}
// Write args.
t := n.Left.Type
args := n.Rlist.Slice()
if n.Op == OCALLMETH {
f := t.Recv()
s.storeArg(args[0], f.Type, argStart+f.Offset)
args = args[1:]
}
for i, n := range args {
f := t.Params().Field(i)
s.storeArg(n, f.Type, argStart+f.Offset)
}
// call target
var call *ssa.Value var call *ssa.Value
switch { if k == callDeferStack {
case k == callDefer: // Make a defer struct d on the stack.
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, deferproc, s.mem()) t := deferstruct(stksize)
case k == callGo: d := tempAt(n.Pos, s.curfn, t)
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, newproc, s.mem())
case closure != nil: s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, d, s.mem())
// rawLoad because loading the code pointer from a addr := s.addr(d, false)
// closure is always safe, but IsSanitizerSafeAddr
// can't always figure that out currently, and it's // Must match reflect.go:deferstruct and src/runtime/runtime2.go:_defer.
// critical that we not clobber any arguments already // 0: siz
// stored onto the stack. s.store(types.Types[TUINT32],
codeptr = s.rawLoad(types.Types[TUINTPTR], closure) s.newValue1I(ssa.OpOffPtr, types.Types[TUINT32].PtrTo(), t.FieldOff(0), addr),
call = s.newValue3(ssa.OpClosureCall, types.TypeMem, codeptr, closure, s.mem()) s.constInt32(types.Types[TUINT32], int32(stksize)))
case codeptr != nil: // 1: started, set in deferprocStack
call = s.newValue2(ssa.OpInterCall, types.TypeMem, codeptr, s.mem()) // 2: heap, set in deferprocStack
case sym != nil: // 3: sp, set in deferprocStack
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, sym.Linksym(), s.mem()) // 4: pc, set in deferprocStack
default: // 5: fn
Fatalf("bad call type %v %v", n.Op, n) s.store(closure.Type,
s.newValue1I(ssa.OpOffPtr, closure.Type.PtrTo(), t.FieldOff(5), addr),
closure)
// 6: panic, set in deferprocStack
// 7: link, set in deferprocStack
// Then, store all the arguments of the defer call.
ft := fn.Type
off := t.FieldOff(8)
args := n.Rlist.Slice()
// Set receiver (for interface calls). Always a pointer.
if rcvr != nil {
p := s.newValue1I(ssa.OpOffPtr, ft.Recv().Type.PtrTo(), off, addr)
s.store(types.Types[TUINTPTR], p, rcvr)
}
// Set receiver (for method calls).
if n.Op == OCALLMETH {
f := ft.Recv()
s.storeArgWithBase(args[0], f.Type, addr, off+f.Offset)
args = args[1:]
}
// Set other args.
for _, f := range ft.Params().Fields().Slice() {
s.storeArgWithBase(args[0], f.Type, addr, off+f.Offset)
args = args[1:]
}
// Call runtime.deferprocStack with pointer to _defer record.
arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize())
s.store(types.Types[TUINTPTR], arg0, addr)
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, deferprocStack, s.mem())
if stksize < int64(Widthptr) {
// We need room for both the call to deferprocStack and the call to
// the deferred function.
stksize = int64(Widthptr)
}
call.AuxInt = stksize
} else {
// Store arguments to stack, including defer/go arguments and receiver for method calls.
// These are written in SP-offset order.
argStart := Ctxt.FixedFrameSize()
// Defer/go args.
if k != callNormal {
// Write argsize and closure (args to newproc/deferproc).
argsize := s.constInt32(types.Types[TUINT32], int32(stksize))
addr := s.constOffPtrSP(s.f.Config.Types.UInt32Ptr, argStart)
s.store(types.Types[TUINT32], addr, argsize)
addr = s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart+int64(Widthptr))
s.store(types.Types[TUINTPTR], addr, closure)
stksize += 2 * int64(Widthptr)
argStart += 2 * int64(Widthptr)
}
// Set receiver (for interface calls).
if rcvr != nil {
addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart)
s.store(types.Types[TUINTPTR], addr, rcvr)
}
// Write args.
t := n.Left.Type
args := n.Rlist.Slice()
if n.Op == OCALLMETH {
f := t.Recv()
s.storeArg(args[0], f.Type, argStart+f.Offset)
args = args[1:]
}
for i, n := range args {
f := t.Params().Field(i)
s.storeArg(n, f.Type, argStart+f.Offset)
}
// call target
switch {
case k == callDefer:
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, deferproc, s.mem())
case k == callGo:
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, newproc, s.mem())
case closure != nil:
// rawLoad because loading the code pointer from a
// closure is always safe, but IsSanitizerSafeAddr
// can't always figure that out currently, and it's
// critical that we not clobber any arguments already
// stored onto the stack.
codeptr = s.rawLoad(types.Types[TUINTPTR], closure)
call = s.newValue3(ssa.OpClosureCall, types.TypeMem, codeptr, closure, s.mem())
case codeptr != nil:
call = s.newValue2(ssa.OpInterCall, types.TypeMem, codeptr, s.mem())
case sym != nil:
call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, sym.Linksym(), s.mem())
default:
Fatalf("bad call type %v %v", n.Op, n)
}
call.AuxInt = stksize // Call operations carry the argsize of the callee along with them
} }
call.AuxInt = stksize // Call operations carry the argsize of the callee along with them
s.vars[&memVar] = call s.vars[&memVar] = call
// Finish block for defers // Finish block for defers
if k == callDefer { if k == callDefer || k == callDeferStack {
b := s.endBlock() b := s.endBlock()
b.Kind = ssa.BlockDefer b.Kind = ssa.BlockDefer
b.SetControl(call) b.SetControl(call)
@ -4357,17 +4425,27 @@ func (s *state) storeTypePtrs(t *types.Type, left, right *ssa.Value) {
} }
func (s *state) storeArg(n *Node, t *types.Type, off int64) { func (s *state) storeArg(n *Node, t *types.Type, off int64) {
s.storeArgWithBase(n, t, s.sp, off)
}
func (s *state) storeArgWithBase(n *Node, t *types.Type, base *ssa.Value, off int64) {
pt := types.NewPtr(t) pt := types.NewPtr(t)
sp := s.constOffPtrSP(pt, off) var addr *ssa.Value
if base == s.sp {
// Use special routine that avoids allocation on duplicate offsets.
addr = s.constOffPtrSP(pt, off)
} else {
addr = s.newValue1I(ssa.OpOffPtr, pt, off, base)
}
if !canSSAType(t) { if !canSSAType(t) {
a := s.addr(n, false) a := s.addr(n, false)
s.move(t, sp, a) s.move(t, addr, a)
return return
} }
a := s.expr(n) a := s.expr(n)
s.storeType(t, sp, a, 0, false) s.storeType(t, addr, a, 0, false)
} }
// slice computes the slice v[i:j:k] and returns ptr, len, and cap of result. // slice computes the slice v[i:j:k] and returns ptr, len, and cap of result.

View file

@ -12,6 +12,7 @@ import (
"cmd/compile/internal/types" "cmd/compile/internal/types"
"cmd/internal/obj" "cmd/internal/obj"
"cmd/internal/src" "cmd/internal/src"
"sort"
) )
// A Node is a single node in the syntax tree. // A Node is a single node in the syntax tree.
@ -598,10 +599,10 @@ const (
OSTR2RUNES // Type(Left) (Type is []rune, Left is a string) OSTR2RUNES // Type(Left) (Type is []rune, Left is a string)
OAS // Left = Right or (if Colas=true) Left := Right OAS // Left = Right or (if Colas=true) Left := Right
OAS2 // List = Rlist (x, y, z = a, b, c) OAS2 // List = Rlist (x, y, z = a, b, c)
OAS2FUNC // List = Rlist (x, y = f())
OAS2RECV // List = Rlist (x, ok = <-c)
OAS2MAPR // List = Rlist (x, ok = m["foo"])
OAS2DOTTYPE // List = Rlist (x, ok = I.(int)) OAS2DOTTYPE // List = Rlist (x, ok = I.(int))
OAS2FUNC // List = Rlist (x, y = f())
OAS2MAPR // List = Rlist (x, ok = m["foo"])
OAS2RECV // List = Rlist (x, ok = <-c)
OASOP // Left Etype= Right (x += y) OASOP // Left Etype= Right (x += y)
OCALL // Left(List) (function call, method call or type conversion) OCALL // Left(List) (function call, method call or type conversion)
@ -970,3 +971,30 @@ func (q *nodeQueue) popLeft() *Node {
q.head++ q.head++
return n return n
} }
// NodeSet is a set of Nodes.
type NodeSet map[*Node]struct{}
// Has reports whether s contains n.
func (s NodeSet) Has(n *Node) bool {
_, isPresent := s[n]
return isPresent
}
// Add adds n to s.
func (s *NodeSet) Add(n *Node) {
if *s == nil {
*s = make(map[*Node]struct{})
}
(*s)[n] = struct{}{}
}
// Sorted returns s sorted according to less.
func (s NodeSet) Sorted(less func(*Node, *Node) bool) []*Node {
var res []*Node
for n := range s {
res = append(res, n)
}
sort.Slice(res, func(i, j int) bool { return less(res[i], res[j]) })
return res
}

View file

@ -1393,7 +1393,7 @@ opswitch:
// Allocate a [n]byte of the right size. // Allocate a [n]byte of the right size.
t := types.NewArray(types.Types[TUINT8], int64(len(sc))) t := types.NewArray(types.Types[TUINT8], int64(len(sc)))
var a *Node var a *Node
if n.Esc == EscNone && len(sc) <= maxImplicitStackVarSize { if n.Esc == EscNone && len(sc) <= int(maxImplicitStackVarSize) {
a = nod(OADDR, temp(t), nil) a = nod(OADDR, temp(t), nil)
} else { } else {
a = callnew(t) a = callnew(t)

View file

@ -800,6 +800,8 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
bne := s.Prog(s390x.ABNE) bne := s.Prog(s390x.ABNE)
bne.To.Type = obj.TYPE_BRANCH bne.To.Type = obj.TYPE_BRANCH
gc.Patch(bne, cs) gc.Patch(bne, cs)
case ssa.OpS390XSYNC:
s.Prog(s390x.ASYNC)
case ssa.OpClobber: case ssa.OpClobber:
// TODO: implement for clobberdead experiment. Nop is ok for now. // TODO: implement for clobberdead experiment. Nop is ok for now.
default: default:

View file

@ -6,6 +6,7 @@ package ssa
import ( import (
"cmd/compile/internal/types" "cmd/compile/internal/types"
"cmd/internal/src"
"testing" "testing"
) )
@ -19,6 +20,7 @@ func TestCSEAuxPartitionBug(t *testing.T) {
arg1Aux := &tstAux{"arg1-aux"} arg1Aux := &tstAux{"arg1-aux"}
arg2Aux := &tstAux{"arg2-aux"} arg2Aux := &tstAux{"arg2-aux"}
arg3Aux := &tstAux{"arg3-aux"} arg3Aux := &tstAux{"arg3-aux"}
a := c.Frontend().Auto(src.NoXPos, c.config.Types.Int8)
// construct lots of values with args that have aux values and place // construct lots of values with args that have aux values and place
// them in an order that triggers the bug // them in an order that triggers the bug
@ -36,7 +38,7 @@ func TestCSEAuxPartitionBug(t *testing.T) {
Valu("r8", OpAdd64, c.config.Types.Int64, 0, nil, "arg3", "arg2"), Valu("r8", OpAdd64, c.config.Types.Int64, 0, nil, "arg3", "arg2"),
Valu("r2", OpAdd64, c.config.Types.Int64, 0, nil, "arg1", "arg2"), Valu("r2", OpAdd64, c.config.Types.Int64, 0, nil, "arg1", "arg2"),
Valu("raddr", OpLocalAddr, c.config.Types.Int64.PtrTo(), 0, nil, "sp", "start"), Valu("raddr", OpLocalAddr, c.config.Types.Int64.PtrTo(), 0, nil, "sp", "start"),
Valu("raddrdef", OpVarDef, types.TypeMem, 0, nil, "start"), Valu("raddrdef", OpVarDef, types.TypeMem, 0, a, "start"),
Valu("r6", OpAdd64, c.config.Types.Int64, 0, nil, "r4", "r5"), Valu("r6", OpAdd64, c.config.Types.Int64, 0, nil, "r4", "r5"),
Valu("r3", OpAdd64, c.config.Types.Int64, 0, nil, "arg1", "arg2"), Valu("r3", OpAdd64, c.config.Types.Int64, 0, nil, "arg1", "arg2"),
Valu("r5", OpAdd64, c.config.Types.Int64, 0, nil, "r2", "r3"), Valu("r5", OpAdd64, c.config.Types.Int64, 0, nil, "r2", "r3"),
@ -89,6 +91,7 @@ func TestCSEAuxPartitionBug(t *testing.T) {
// TestZCSE tests the zero arg cse. // TestZCSE tests the zero arg cse.
func TestZCSE(t *testing.T) { func TestZCSE(t *testing.T) {
c := testConfig(t) c := testConfig(t)
a := c.Frontend().Auto(src.NoXPos, c.config.Types.Int8)
fun := c.Fun("entry", fun := c.Fun("entry",
Bloc("entry", Bloc("entry",
@ -106,7 +109,7 @@ func TestZCSE(t *testing.T) {
Valu("r2", OpAdd64, c.config.Types.Int64, 0, nil, "a2ld", "c2"), Valu("r2", OpAdd64, c.config.Types.Int64, 0, nil, "a2ld", "c2"),
Valu("r3", OpAdd64, c.config.Types.Int64, 0, nil, "r1", "r2"), Valu("r3", OpAdd64, c.config.Types.Int64, 0, nil, "r1", "r2"),
Valu("raddr", OpLocalAddr, c.config.Types.Int64.PtrTo(), 0, nil, "sp", "start"), Valu("raddr", OpLocalAddr, c.config.Types.Int64.PtrTo(), 0, nil, "sp", "start"),
Valu("raddrdef", OpVarDef, types.TypeMem, 0, nil, "start"), Valu("raddrdef", OpVarDef, types.TypeMem, 0, a, "start"),
Valu("rstore", OpStore, types.TypeMem, 0, c.config.Types.Int64, "raddr", "r3", "raddrdef"), Valu("rstore", OpStore, types.TypeMem, 0, c.config.Types.Int64, "raddr", "r3", "raddrdef"),
Goto("exit")), Goto("exit")),
Bloc("exit", Bloc("exit",

View file

@ -66,7 +66,7 @@ func fuseBlockIf(b *Block) bool {
var ss0, ss1 *Block var ss0, ss1 *Block
s0 := b.Succs[0].b s0 := b.Succs[0].b
i0 := b.Succs[0].i i0 := b.Succs[0].i
if s0.Kind != BlockPlain || len(s0.Preds) != 1 || len(s0.Values) != 0 { if s0.Kind != BlockPlain || len(s0.Preds) != 1 || !isEmpty(s0) {
s0, ss0 = b, s0 s0, ss0 = b, s0
} else { } else {
ss0 = s0.Succs[0].b ss0 = s0.Succs[0].b
@ -74,7 +74,7 @@ func fuseBlockIf(b *Block) bool {
} }
s1 := b.Succs[1].b s1 := b.Succs[1].b
i1 := b.Succs[1].i i1 := b.Succs[1].i
if s1.Kind != BlockPlain || len(s1.Preds) != 1 || len(s1.Values) != 0 { if s1.Kind != BlockPlain || len(s1.Preds) != 1 || !isEmpty(s1) {
s1, ss1 = b, s1 s1, ss1 = b, s1
} else { } else {
ss1 = s1.Succs[0].b ss1 = s1.Succs[0].b
@ -120,18 +120,34 @@ func fuseBlockIf(b *Block) bool {
b.Likely = BranchUnknown b.Likely = BranchUnknown
b.SetControl(nil) b.SetControl(nil)
// Trash the empty blocks s0 & s1. // Trash the empty blocks s0 and s1.
if s0 != b { blocks := [...]*Block{s0, s1}
s0.Kind = BlockInvalid for _, s := range &blocks {
s0.Values = nil if s == b {
s0.Succs = nil continue
s0.Preds = nil }
// Move any (dead) values in s0 or s1 to b,
// where they will be eliminated by the next deadcode pass.
for _, v := range s.Values {
v.Block = b
}
b.Values = append(b.Values, s.Values...)
// Clear s.
s.Kind = BlockInvalid
s.Values = nil
s.Succs = nil
s.Preds = nil
} }
if s1 != b { return true
s1.Kind = BlockInvalid }
s1.Values = nil
s1.Succs = nil // isEmpty reports whether b contains any live values.
s1.Preds = nil // There may be false positives.
func isEmpty(b *Block) bool {
for _, v := range b.Values {
if v.Uses > 0 || v.Type.IsVoid() {
return false
}
} }
return true return true
} }

View file

@ -139,16 +139,15 @@
(RoundToEven x) -> (FIDBR [4] x) (RoundToEven x) -> (FIDBR [4] x)
(Round x) -> (FIDBR [1] x) (Round x) -> (FIDBR [1] x)
// Atomic loads. // Atomic loads and stores.
(AtomicLoad8 ptr mem) -> (MOVBZatomicload ptr mem) // The SYNC instruction (fast-BCR-serialization) prevents store-load
(AtomicLoad32 ptr mem) -> (MOVWZatomicload ptr mem) // reordering. Other sequences of memory operations (load-load,
(AtomicLoad64 ptr mem) -> (MOVDatomicload ptr mem) // store-store and load-store) are already guaranteed not to be reordered.
(AtomicLoadPtr ptr mem) -> (MOVDatomicload ptr mem) (AtomicLoad(8|32|Acq32|64|Ptr) ptr mem) -> (MOV(BZ|WZ|WZ|D|D)atomicload ptr mem)
(AtomicStore(32|64|PtrNoWB) ptr val mem) -> (SYNC (MOV(W|D|D)atomicstore ptr val mem))
// Atomic stores. // Store-release doesn't require store-load ordering.
(AtomicStore32 ptr val mem) -> (MOVWatomicstore ptr val mem) (AtomicStoreRel32 ptr val mem) -> (MOVWatomicstore ptr val mem)
(AtomicStore64 ptr val mem) -> (MOVDatomicstore ptr val mem)
(AtomicStorePtrNoWB ptr val mem) -> (MOVDatomicstore ptr val mem)
// Atomic adds. // Atomic adds.
(AtomicAdd32 ptr val mem) -> (AddTupleFirst32 val (LAA ptr val mem)) (AtomicAdd32 ptr val mem) -> (AddTupleFirst32 val (LAA ptr val mem))

View file

@ -187,6 +187,8 @@ func init() {
fpstore = regInfo{inputs: []regMask{ptrspsb, fp, 0}} fpstore = regInfo{inputs: []regMask{ptrspsb, fp, 0}}
fpstoreidx = regInfo{inputs: []regMask{ptrsp, ptrsp, fp, 0}} fpstoreidx = regInfo{inputs: []regMask{ptrsp, ptrsp, fp, 0}}
sync = regInfo{inputs: []regMask{0}}
// LoweredAtomicCas may overwrite arg1, so force it to R0 for now. // LoweredAtomicCas may overwrite arg1, so force it to R0 for now.
cas = regInfo{inputs: []regMask{ptrsp, r0, gpsp, 0}, outputs: []regMask{gp, 0}, clobbers: r0} cas = regInfo{inputs: []regMask{ptrsp, r0, gpsp, 0}, outputs: []regMask{gp, 0}, clobbers: r0}
@ -493,6 +495,9 @@ func init() {
{name: "FlagGT"}, // CC=2 (greater than) {name: "FlagGT"}, // CC=2 (greater than)
{name: "FlagOV"}, // CC=3 (overflow) {name: "FlagOV"}, // CC=3 (overflow)
// Fast-BCR-serialization to ensure store-load ordering.
{name: "SYNC", argLength: 1, reg: sync, asm: "SYNC", typ: "Mem"},
// Atomic loads. These are just normal loads but return <value,memory> tuples // Atomic loads. These are just normal loads but return <value,memory> tuples
// so they can be properly ordered with other loads. // so they can be properly ordered with other loads.
// load from arg0+auxint+aux. arg1=mem. // load from arg0+auxint+aux. arg1=mem.

View file

@ -46,6 +46,7 @@ func TestLoopConditionS390X(t *testing.T) {
// done: // done:
// //
c := testConfigS390X(t) c := testConfigS390X(t)
a := c.Frontend().Auto(src.NoXPos, c.config.Types.Int8)
fun := c.Fun("entry", fun := c.Fun("entry",
Bloc("entry", Bloc("entry",
Valu("mem", OpInitMem, types.TypeMem, 0, nil), Valu("mem", OpInitMem, types.TypeMem, 0, nil),
@ -67,7 +68,7 @@ func TestLoopConditionS390X(t *testing.T) {
Valu("sum", OpAdd64, c.config.Types.Int64, 0, nil, "phisum", "c3"), Valu("sum", OpAdd64, c.config.Types.Int64, 0, nil, "phisum", "c3"),
Goto("b1")), Goto("b1")),
Bloc("b3", Bloc("b3",
Valu("retdef", OpVarDef, types.TypeMem, 0, nil, "mem"), Valu("retdef", OpVarDef, types.TypeMem, 0, a, "mem"),
Valu("store", OpStore, types.TypeMem, 0, c.config.Types.Int64, "ret", "phisum", "retdef"), Valu("store", OpStore, types.TypeMem, 0, c.config.Types.Int64, "ret", "phisum", "retdef"),
Exit("store"))) Exit("store")))
CheckFunc(fun.f) CheckFunc(fun.f)

View file

@ -219,9 +219,31 @@ func nilcheckelim2(f *Func) {
continue continue
} }
if v.Type.IsMemory() || v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() { if v.Type.IsMemory() || v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() {
if v.Op == OpVarDef || v.Op == OpVarKill || v.Op == OpVarLive { if v.Op == OpVarKill || v.Op == OpVarLive || (v.Op == OpVarDef && !v.Aux.(GCNode).Typ().HasHeapPointer()) {
// These ops don't really change memory. // These ops don't really change memory.
continue continue
// Note: OpVarDef requires that the defined variable not have pointers.
// We need to make sure that there's no possible faulting
// instruction between a VarDef and that variable being
// fully initialized. If there was, then anything scanning
// the stack during the handling of that fault will see
// a live but uninitialized pointer variable on the stack.
//
// If we have:
//
// NilCheck p
// VarDef x
// x = *p
//
// We can't rewrite that to
//
// VarDef x
// NilCheck p
// x = *p
//
// Particularly, even though *p faults on p==nil, we still
// have to do the explicit nil check before the VarDef.
// See issue #32288.
} }
// This op changes memory. Any faulting instruction after v that // This op changes memory. Any faulting instruction after v that
// we've recorded in the unnecessary map is now obsolete. // we've recorded in the unnecessary map is now obsolete.

View file

@ -2054,6 +2054,7 @@ const (
OpS390XFlagLT OpS390XFlagLT
OpS390XFlagGT OpS390XFlagGT
OpS390XFlagOV OpS390XFlagOV
OpS390XSYNC
OpS390XMOVBZatomicload OpS390XMOVBZatomicload
OpS390XMOVWZatomicload OpS390XMOVWZatomicload
OpS390XMOVDatomicload OpS390XMOVDatomicload
@ -27614,6 +27615,12 @@ var opcodeTable = [...]opInfo{
argLen: 0, argLen: 0,
reg: regInfo{}, reg: regInfo{},
}, },
{
name: "SYNC",
argLen: 1,
asm: s390x.ASYNC,
reg: regInfo{},
},
{ {
name: "MOVBZatomicload", name: "MOVBZatomicload",
auxType: auxSymOff, auxType: auxSymOff,

View file

@ -553,15 +553,29 @@ func (ft *factsTable) isNonNegative(v *Value) bool {
return true return true
} }
var max int64
switch v.Type.Size() {
case 1:
max = math.MaxInt8
case 2:
max = math.MaxInt16
case 4:
max = math.MaxInt32
case 8:
max = math.MaxInt64
default:
panic("unexpected integer size")
}
// Check if the recorded limits can prove that the value is positive // Check if the recorded limits can prove that the value is positive
if l, has := ft.limits[v.ID]; has && (l.min >= 0 || l.umax <= math.MaxInt64) { if l, has := ft.limits[v.ID]; has && (l.min >= 0 || l.umax <= uint64(max)) {
return true return true
} }
// Check if v = x+delta, and we can use x's limits to prove that it's positive // Check if v = x+delta, and we can use x's limits to prove that it's positive
if x, delta := isConstDelta(v); x != nil { if x, delta := isConstDelta(v); x != nil {
if l, has := ft.limits[x.ID]; has { if l, has := ft.limits[x.ID]; has {
if delta > 0 && l.min >= -delta && l.max <= math.MaxInt64-delta { if delta > 0 && l.min >= -delta && l.max <= max-delta {
return true return true
} }
if delta < 0 && l.min >= -delta { if delta < 0 && l.min >= -delta {

View file

@ -61,6 +61,8 @@ func rewriteValueS390X(v *Value) bool {
return rewriteValueS390X_OpAtomicLoad64_0(v) return rewriteValueS390X_OpAtomicLoad64_0(v)
case OpAtomicLoad8: case OpAtomicLoad8:
return rewriteValueS390X_OpAtomicLoad8_0(v) return rewriteValueS390X_OpAtomicLoad8_0(v)
case OpAtomicLoadAcq32:
return rewriteValueS390X_OpAtomicLoadAcq32_0(v)
case OpAtomicLoadPtr: case OpAtomicLoadPtr:
return rewriteValueS390X_OpAtomicLoadPtr_0(v) return rewriteValueS390X_OpAtomicLoadPtr_0(v)
case OpAtomicStore32: case OpAtomicStore32:
@ -69,6 +71,8 @@ func rewriteValueS390X(v *Value) bool {
return rewriteValueS390X_OpAtomicStore64_0(v) return rewriteValueS390X_OpAtomicStore64_0(v)
case OpAtomicStorePtrNoWB: case OpAtomicStorePtrNoWB:
return rewriteValueS390X_OpAtomicStorePtrNoWB_0(v) return rewriteValueS390X_OpAtomicStorePtrNoWB_0(v)
case OpAtomicStoreRel32:
return rewriteValueS390X_OpAtomicStoreRel32_0(v)
case OpAvg64u: case OpAvg64u:
return rewriteValueS390X_OpAvg64u_0(v) return rewriteValueS390X_OpAvg64u_0(v)
case OpBitLen64: case OpBitLen64:
@ -1132,6 +1136,19 @@ func rewriteValueS390X_OpAtomicLoad8_0(v *Value) bool {
return true return true
} }
} }
func rewriteValueS390X_OpAtomicLoadAcq32_0(v *Value) bool {
// match: (AtomicLoadAcq32 ptr mem)
// cond:
// result: (MOVWZatomicload ptr mem)
for {
mem := v.Args[1]
ptr := v.Args[0]
v.reset(OpS390XMOVWZatomicload)
v.AddArg(ptr)
v.AddArg(mem)
return true
}
}
func rewriteValueS390X_OpAtomicLoadPtr_0(v *Value) bool { func rewriteValueS390X_OpAtomicLoadPtr_0(v *Value) bool {
// match: (AtomicLoadPtr ptr mem) // match: (AtomicLoadPtr ptr mem)
// cond: // cond:
@ -1146,8 +1163,62 @@ func rewriteValueS390X_OpAtomicLoadPtr_0(v *Value) bool {
} }
} }
func rewriteValueS390X_OpAtomicStore32_0(v *Value) bool { func rewriteValueS390X_OpAtomicStore32_0(v *Value) bool {
b := v.Block
// match: (AtomicStore32 ptr val mem) // match: (AtomicStore32 ptr val mem)
// cond: // cond:
// result: (SYNC (MOVWatomicstore ptr val mem))
for {
mem := v.Args[2]
ptr := v.Args[0]
val := v.Args[1]
v.reset(OpS390XSYNC)
v0 := b.NewValue0(v.Pos, OpS390XMOVWatomicstore, types.TypeMem)
v0.AddArg(ptr)
v0.AddArg(val)
v0.AddArg(mem)
v.AddArg(v0)
return true
}
}
func rewriteValueS390X_OpAtomicStore64_0(v *Value) bool {
b := v.Block
// match: (AtomicStore64 ptr val mem)
// cond:
// result: (SYNC (MOVDatomicstore ptr val mem))
for {
mem := v.Args[2]
ptr := v.Args[0]
val := v.Args[1]
v.reset(OpS390XSYNC)
v0 := b.NewValue0(v.Pos, OpS390XMOVDatomicstore, types.TypeMem)
v0.AddArg(ptr)
v0.AddArg(val)
v0.AddArg(mem)
v.AddArg(v0)
return true
}
}
func rewriteValueS390X_OpAtomicStorePtrNoWB_0(v *Value) bool {
b := v.Block
// match: (AtomicStorePtrNoWB ptr val mem)
// cond:
// result: (SYNC (MOVDatomicstore ptr val mem))
for {
mem := v.Args[2]
ptr := v.Args[0]
val := v.Args[1]
v.reset(OpS390XSYNC)
v0 := b.NewValue0(v.Pos, OpS390XMOVDatomicstore, types.TypeMem)
v0.AddArg(ptr)
v0.AddArg(val)
v0.AddArg(mem)
v.AddArg(v0)
return true
}
}
func rewriteValueS390X_OpAtomicStoreRel32_0(v *Value) bool {
// match: (AtomicStoreRel32 ptr val mem)
// cond:
// result: (MOVWatomicstore ptr val mem) // result: (MOVWatomicstore ptr val mem)
for { for {
mem := v.Args[2] mem := v.Args[2]
@ -1160,36 +1231,6 @@ func rewriteValueS390X_OpAtomicStore32_0(v *Value) bool {
return true return true
} }
} }
func rewriteValueS390X_OpAtomicStore64_0(v *Value) bool {
// match: (AtomicStore64 ptr val mem)
// cond:
// result: (MOVDatomicstore ptr val mem)
for {
mem := v.Args[2]
ptr := v.Args[0]
val := v.Args[1]
v.reset(OpS390XMOVDatomicstore)
v.AddArg(ptr)
v.AddArg(val)
v.AddArg(mem)
return true
}
}
func rewriteValueS390X_OpAtomicStorePtrNoWB_0(v *Value) bool {
// match: (AtomicStorePtrNoWB ptr val mem)
// cond:
// result: (MOVDatomicstore ptr val mem)
for {
mem := v.Args[2]
ptr := v.Args[0]
val := v.Args[1]
v.reset(OpS390XMOVDatomicstore)
v.AddArg(ptr)
v.AddArg(val)
v.AddArg(mem)
return true
}
}
func rewriteValueS390X_OpAvg64u_0(v *Value) bool { func rewriteValueS390X_OpAvg64u_0(v *Value) bool {
b := v.Block b := v.Block
// match: (Avg64u <t> x y) // match: (Avg64u <t> x y)

View file

@ -386,6 +386,9 @@ func (f *File) addCounters(pos, insertPos, blockEnd token.Pos, list []ast.Stmt,
f.edit.Insert(f.offset(insertPos), f.newCounter(insertPos, blockEnd, 0)+";") f.edit.Insert(f.offset(insertPos), f.newCounter(insertPos, blockEnd, 0)+";")
return return
} }
// Make a copy of the list, as we may mutate it and should leave the
// existing list intact.
list = append([]ast.Stmt(nil), list...)
// We have a block (statement list), but it may have several basic blocks due to the // We have a block (statement list), but it may have several basic blocks due to the
// appearance of statements that affect the flow of control. // appearance of statements that affect the flow of control.
for { for {

View file

@ -132,6 +132,10 @@ func testBlockRun() {
func testSwitch() { func testSwitch() {
for i := 0; i < 5; func() { i++; check(LINE, 5) }() { for i := 0; i < 5; func() { i++; check(LINE, 5) }() {
goto label2
label1:
goto label1
label2:
switch i { switch i {
case 0: case 0:
check(LINE, 1) check(LINE, 1)

View file

@ -866,13 +866,6 @@ func runInstall(dir string, ch chan struct{}) {
if symabis != "" { if symabis != "" {
compile = append(compile, "-symabis", symabis) compile = append(compile, "-symabis", symabis)
} }
if dir == "runtime" || dir == "runtime/internal/atomic" {
// These packages define symbols referenced by
// assembly in other packages. In cmd/go, we work out
// the exact details. For bootstrapping, just tell the
// compiler to generate ABI wrappers for everything.
compile = append(compile, "-allabis")
}
if goos == "android" { if goos == "android" {
compile = append(compile, "-shared") compile = append(compile, "-shared")
} }

View file

@ -31,18 +31,36 @@ const (
) )
type Package struct { type Package struct {
writer io.Writer // Destination for output. writer io.Writer // Destination for output.
name string // Package name, json for encoding/json. name string // Package name, json for encoding/json.
userPath string // String the user used to find this package. userPath string // String the user used to find this package.
pkg *ast.Package // Parsed package. pkg *ast.Package // Parsed package.
file *ast.File // Merged from all files in the package file *ast.File // Merged from all files in the package
doc *doc.Package doc *doc.Package
build *build.Package build *build.Package
typedValue map[*doc.Value]bool // Consts and vars related to types. typedValue map[*doc.Value]bool // Consts and vars related to types.
constructor map[*doc.Func]bool // Constructors. constructor map[*doc.Func]bool // Constructors.
packageClausePrinted bool // Prevent repeated package clauses. fs *token.FileSet // Needed for printing.
fs *token.FileSet // Needed for printing. buf pkgBuffer
buf bytes.Buffer }
// pkgBuffer is a wrapper for bytes.Buffer that prints a package clause the
// first time Write is called.
type pkgBuffer struct {
pkg *Package
printed bool // Prevent repeated package clauses.
bytes.Buffer
}
func (pb *pkgBuffer) Write(p []byte) (int, error) {
if !pb.printed && len(p) > 0 {
pb.printed = true
// Only show package clause for commands if requested explicitly.
if pb.pkg.pkg.Name != "main" || showCmd {
pb.pkg.packageClause()
}
}
return pb.Buffer.Write(p)
} }
type PackageError string // type returned by pkg.Fatalf. type PackageError string // type returned by pkg.Fatalf.
@ -129,7 +147,10 @@ func parsePackage(writer io.Writer, pkg *build.Package, userPath string) *Packag
log.Fatal(err) log.Fatal(err)
} }
// Make sure they are all in one package. // Make sure they are all in one package.
if len(pkgs) != 1 { if len(pkgs) == 0 {
log.Fatalf("no source-code package in directory %s", pkg.Dir)
}
if len(pkgs) > 1 {
log.Fatalf("multiple packages in directory %s", pkg.Dir) log.Fatalf("multiple packages in directory %s", pkg.Dir)
} }
astPkg := pkgs[pkg.Name] astPkg := pkgs[pkg.Name]
@ -168,7 +189,7 @@ func parsePackage(writer io.Writer, pkg *build.Package, userPath string) *Packag
} }
} }
return &Package{ p := &Package{
writer: writer, writer: writer,
name: pkg.Name, name: pkg.Name,
userPath: userPath, userPath: userPath,
@ -180,6 +201,8 @@ func parsePackage(writer io.Writer, pkg *build.Package, userPath string) *Packag
build: pkg, build: pkg,
fs: fs, fs: fs,
} }
p.buf.pkg = p
return p
} }
func (pkg *Package) Printf(format string, args ...interface{}) { func (pkg *Package) Printf(format string, args ...interface{}) {
@ -423,9 +446,6 @@ func joinStrings(ss []string) string {
// allDoc prints all the docs for the package. // allDoc prints all the docs for the package.
func (pkg *Package) allDoc() { func (pkg *Package) allDoc() {
defer pkg.flush() defer pkg.flush()
if pkg.showInternals() {
pkg.packageClause(false)
}
doc.ToText(&pkg.buf, pkg.doc.Doc, "", indent, indentedWidth) doc.ToText(&pkg.buf, pkg.doc.Doc, "", indent, indentedWidth)
pkg.newlines(1) pkg.newlines(1)
@ -486,14 +506,11 @@ func (pkg *Package) allDoc() {
// packageDoc prints the docs for the package (package doc plus one-liners of the rest). // packageDoc prints the docs for the package (package doc plus one-liners of the rest).
func (pkg *Package) packageDoc() { func (pkg *Package) packageDoc() {
defer pkg.flush() defer pkg.flush()
if pkg.showInternals() {
pkg.packageClause(false)
}
doc.ToText(&pkg.buf, pkg.doc.Doc, "", indent, indentedWidth) doc.ToText(&pkg.buf, pkg.doc.Doc, "", indent, indentedWidth)
pkg.newlines(1) pkg.newlines(1)
if !pkg.showInternals() { if pkg.pkg.Name == "main" && !showCmd {
// Show only package docs for commands. // Show only package docs for commands.
return return
} }
@ -506,29 +523,8 @@ func (pkg *Package) packageDoc() {
pkg.bugs() pkg.bugs()
} }
// showInternals reports whether we should show the internals
// of a package as opposed to just the package docs.
// Used to decide whether to suppress internals for commands.
// Called only by Package.packageDoc.
func (pkg *Package) showInternals() bool {
return pkg.pkg.Name != "main" || showCmd
}
// packageClause prints the package clause. // packageClause prints the package clause.
// The argument boolean, if true, suppresses the output if the func (pkg *Package) packageClause() {
// user's argument is identical to the actual package path or
// is empty, meaning it's the current directory.
func (pkg *Package) packageClause(checkUserPath bool) {
if pkg.packageClausePrinted {
return
}
if checkUserPath {
if pkg.userPath == "" || pkg.userPath == pkg.build.ImportPath {
return
}
}
importPath := pkg.build.ImportComment importPath := pkg.build.ImportComment
if importPath == "" { if importPath == "" {
importPath = pkg.build.ImportPath importPath = pkg.build.ImportPath
@ -560,7 +556,6 @@ func (pkg *Package) packageClause(checkUserPath bool) {
if !usingModules && importPath != pkg.build.ImportPath { if !usingModules && importPath != pkg.build.ImportPath {
pkg.Printf("WARNING: package source is installed in %q\n", pkg.build.ImportPath) pkg.Printf("WARNING: package source is installed in %q\n", pkg.build.ImportPath)
} }
pkg.packageClausePrinted = true
} }
// valueSummary prints a one-line summary for each set of values and constants. // valueSummary prints a one-line summary for each set of values and constants.
@ -698,9 +693,6 @@ func (pkg *Package) symbolDoc(symbol string) bool {
found := false found := false
// Functions. // Functions.
for _, fun := range pkg.findFuncs(symbol) { for _, fun := range pkg.findFuncs(symbol) {
if !found {
pkg.packageClause(true)
}
// Symbol is a function. // Symbol is a function.
decl := fun.Decl decl := fun.Decl
pkg.emit(fun.Doc, decl) pkg.emit(fun.Doc, decl)

View file

@ -3,10 +3,10 @@ module cmd
go 1.12 go 1.12
require ( require (
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57 github.com/google/pprof v0.0.0-20190515194954-54271f7e092f
github.com/ianlancetaylor/demangle v0.0.0-20180524225900-fc6590592b44 // indirect github.com/ianlancetaylor/demangle v0.0.0-20180524225900-fc6590592b44 // indirect
golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045 golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 // indirect golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 // indirect
golang.org/x/tools v0.0.0-20190514135123-4789ca9922f0 golang.org/x/tools v0.0.0-20190611154301-25a4f137592f
) )

View file

@ -1,5 +1,5 @@
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57 h1:eqyIo2HjKhKe/mJzTG8n4VqvLXIOEG+SLdDqX7xGtkY= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f h1:Jnx61latede7zDD3DiiP4gmNz33uK0U5HDUaF0a/HVQ=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/ianlancetaylor/demangle v0.0.0-20180524225900-fc6590592b44 h1:pKqc8lAAA6rcwpvsephnRuZp4VHbfszZRClvqAE6Sq8= github.com/ianlancetaylor/demangle v0.0.0-20180524225900-fc6590592b44 h1:pKqc8lAAA6rcwpvsephnRuZp4VHbfszZRClvqAE6Sq8=
github.com/ianlancetaylor/demangle v0.0.0-20180524225900-fc6590592b44/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20180524225900-fc6590592b44/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045 h1:Pn8fQdvx+z1avAi7fdM2kRYWQNxGlavNDSyzrQg2SsU= golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045 h1:Pn8fQdvx+z1avAi7fdM2kRYWQNxGlavNDSyzrQg2SsU=
@ -13,11 +13,5 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 h1:vsphBvatvfbhlb4PO1BYSr9dzugGxJ/SQHoNufZJq1w= golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 h1:vsphBvatvfbhlb4PO1BYSr9dzugGxJ/SQHoNufZJq1w=
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190509153222-73554e0f7805 h1:1ufBXAsTpUhSmmPXEEs5PrGQSfnBhsjAd2SmVhp9xrY= golang.org/x/tools v0.0.0-20190611154301-25a4f137592f h1:6awn5JC4pwVI5HiBqs7MDtRxnwV9PpO5iSA9v6P09pA=
golang.org/x/tools v0.0.0-20190509153222-73554e0f7805/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190611154301-25a4f137592f/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190510144052-35884eef200b h1:4muk7BhMes67ZgDeK3n4Jvi+FvNDRZzh6ZRqIXZNYwQ=
golang.org/x/tools v0.0.0-20190510144052-35884eef200b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190513233021-7d589f28aaf4 h1:sIGsLZaMtLBc5sLK7s2xtr7VaKk8h31mrJyHwEZq2WQ=
golang.org/x/tools v0.0.0-20190513233021-7d589f28aaf4/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190514135123-4789ca9922f0 h1:0Bz67IMuNMofIoO/F+rX8oPltlfrAC5HU68DEyynMQg=
golang.org/x/tools v0.0.0-20190514135123-4789ca9922f0/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=

View file

@ -47,8 +47,9 @@
// importpath import path syntax // importpath import path syntax
// modules modules, module versions, and more // modules modules, module versions, and more
// module-get module-aware go get // module-get module-aware go get
// packages package lists and patterns
// module-auth module authentication using go.sum // module-auth module authentication using go.sum
// module-private module configuration for non-public modules
// packages package lists and patterns
// testflag testing flags // testflag testing flags
// testfunc testing functions // testfunc testing functions
// //
@ -557,7 +558,7 @@
// //
// Usage: // Usage:
// //
// go get [-d] [-m] [-t] [-u] [-v] [-insecure] [build flags] [packages] // go get [-d] [-t] [-u] [-v] [-insecure] [build flags] [packages]
// //
// Get resolves and adds dependencies to the current development module // Get resolves and adds dependencies to the current development module
// and then builds and installs them. // and then builds and installs them.
@ -585,11 +586,12 @@
// depending on it as needed. // depending on it as needed.
// //
// The version suffix @latest explicitly requests the latest minor release of the // The version suffix @latest explicitly requests the latest minor release of the
// given path. // given path. The suffix @patch requests the latest patch release: if the path
// // is already in the build list, the selected version will have the same minor
// The suffix @patch requests the latest patch release: if the path is already in // version. If the path is not already in the build list, @patch is equivalent
// the build list, the selected version will have the same minor version. // to @latest. Neither @latest nor @patch will cause 'go get' to downgrade a module
// If the path is not already in the build list, @patch is equivalent to @latest. // in the build list if it is required at a newer pre-release version that is
// newer than the latest released version.
// //
// Although get defaults to using the latest version of the module containing // Although get defaults to using the latest version of the module containing
// a named package, it does not use the latest version of that module's // a named package, it does not use the latest version of that module's
@ -603,9 +605,12 @@
// The -t flag instructs get to consider modules needed to build tests of // The -t flag instructs get to consider modules needed to build tests of
// packages specified on the command line. // packages specified on the command line.
// //
// The -u flag instructs get to update dependencies to use newer minor or // The -u flag instructs get to update modules providing dependencies
// patch releases when available. Continuing the previous example, // of packages named on the command line to use newer minor or patch
// 'go get -u A' will use the latest A with B v1.3.1 (not B v1.2.3). // releases when available. Continuing the previous example, 'go get -u A'
// will use the latest A with B v1.3.1 (not B v1.2.3). If B requires module C,
// but C does not provide any packages needed to build packages in A
// (not including tests), then C will not be updated.
// //
// The -u=patch flag (not -u patch) also instructs get to update dependencies, // The -u=patch flag (not -u patch) also instructs get to update dependencies,
// but changes the default to select patch releases. // but changes the default to select patch releases.
@ -622,18 +627,6 @@
// require downgrading other dependencies, and 'go get' does // require downgrading other dependencies, and 'go get' does
// this automatically as well. // this automatically as well.
// //
// The -m flag instructs get to stop here, after resolving, upgrading,
// and downgrading modules and updating go.mod. When using -m,
// each specified package path must be a module path as well,
// not the import path of a package below the module root.
//
// When the -m and -u flags are used together, 'go get' will upgrade
// modules that provide packages depended on by the modules named on
// the command line. For example, 'go get -u -m A' will upgrade A and
// any module providing packages imported by packages in A.
// 'go get -u -m' will upgrade modules that provided packages needed
// by the main module.
//
// The -insecure flag permits fetching from repositories and resolving // The -insecure flag permits fetching from repositories and resolving
// custom domains using insecure schemes such as HTTP. Use with caution. // custom domains using insecure schemes such as HTTP. Use with caution.
// //
@ -681,6 +674,15 @@
// //
// Install compiles and installs the packages named by the import paths. // Install compiles and installs the packages named by the import paths.
// //
// Executables are installed in the directory named by the GOBIN environment
// variable, which defaults to $GOPATH/bin or $HOME/go/bin if the GOPATH
// environment variable is not set. Executables in $GOROOT
// are installed in $GOROOT/bin or $GOTOOLDIR instead of $GOBIN.
//
// When module-aware mode is disabled, other packages are installed in the
// directory $GOPATH/pkg/$GOOS_$GOARCH. When module-aware mode is enabled,
// other packages are built and cached but not installed.
//
// The -i flag installs the dependencies of the named packages as well. // The -i flag installs the dependencies of the named packages as well.
// //
// For more about the build flags, see 'go help build'. // For more about the build flags, see 'go help build'.
@ -1576,9 +1578,17 @@
// GOPATH // GOPATH
// For more details see: 'go help gopath'. // For more details see: 'go help gopath'.
// GOPROXY // GOPROXY
// URL of Go module proxy. See 'go help goproxy'. // URL of Go module proxy. See 'go help modules'.
// GOPRIVATE, GONOPROXY, GONOSUMDB
// Comma-separated list of glob patterns (in the syntax of Go's path.Match)
// of module path prefixes that should always be fetched directly
// or that should not be compared against the checksum database.
// See 'go help module-private'.
// GOROOT // GOROOT
// The root of the go tree. // The root of the go tree.
// GOSUMDB
// The name of checksum database to use and optionally its public key and
// URL. See 'go help module-auth'.
// GOTMPDIR // GOTMPDIR
// The directory where the go command will write // The directory where the go command will write
// temporary source files, packages, and binaries. // temporary source files, packages, and binaries.
@ -2269,23 +2279,26 @@
// //
// Module support // Module support
// //
// Go 1.13 includes official support for Go modules, // Go 1.13 includes support for Go modules. Module-aware mode is active by default
// including a module-aware 'go get' command. // whenever a go.mod file is found in, or in a parent of, the current directory.
// Module-aware mode is active by default. //
// The quickest way to take advantage of module support is to check out your
// repository, create a go.mod file (described in the next section) there, and run
// go commands from within that file tree.
// //
// For more fine-grained control, Go 1.13 continues to respect // For more fine-grained control, Go 1.13 continues to respect
// a temporary environment variable, GO111MODULE, which can be set to one // a temporary environment variable, GO111MODULE, which can be set to one
// of three string values: off, auto, or on (the default). // of three string values: off, on, or auto (the default).
// If GO111MODULE=on or is unset, then the go command requires the use of // If GO111MODULE=on, then the go command requires the use of modules,
// modules, never consulting GOPATH. We refer to this as the command // never consulting GOPATH. We refer to this as the command
// being module-aware or running in "module-aware mode". // being module-aware or running in "module-aware mode".
// If GO111MODULE=auto, then the go command enables or disables module
// support based on the current directory. Module support is enabled only
// when the current directory is outside GOPATH/src and itself contains a
// go.mod file or is below a directory containing a go.mod file.
// If GO111MODULE=off, then the go command never uses // If GO111MODULE=off, then the go command never uses
// module support. Instead it looks in vendor directories and GOPATH // module support. Instead it looks in vendor directories and GOPATH
// to find dependencies; we now refer to this as "GOPATH mode." // to find dependencies; we now refer to this as "GOPATH mode."
// If GO111MODULE=auto or is unset, then the go command enables or disables
// module support based on the current directory.
// Module support is enabled only when the current directory contains a
// go.mod file or is below a directory containing a go.mod file.
// //
// In module-aware mode, GOPATH no longer defines the meaning of imports // In module-aware mode, GOPATH no longer defines the meaning of imports
// during a build, but it still stores downloaded dependencies (in GOPATH/pkg/mod) // during a build, but it still stores downloaded dependencies (in GOPATH/pkg/mod)
@ -2579,7 +2592,9 @@
// The go command can fetch modules from a proxy or connect to source control // The go command can fetch modules from a proxy or connect to source control
// servers directly, according to the setting of the GOPROXY environment // servers directly, according to the setting of the GOPROXY environment
// variable (see 'go help env'). The default setting for GOPROXY is // variable (see 'go help env'). The default setting for GOPROXY is
// "https://proxy.golang.org", the Go module mirror run by Google. // "https://proxy.golang.org,direct", which means to try the
// Go module mirror run by Google and fall back to a direct connection
// if the proxy reports that it does not have the module (HTTP error 404 or 410).
// See https://proxy.golang.org/privacy for the service's privacy policy. // See https://proxy.golang.org/privacy for the service's privacy policy.
// If GOPROXY is set to the string "direct", downloads use a direct connection // If GOPROXY is set to the string "direct", downloads use a direct connection
// to source control servers. Setting GOPROXY to "off" disallows downloading // to source control servers. Setting GOPROXY to "off" disallows downloading
@ -2591,25 +2606,15 @@
// to cause a direct connection to be attempted at that point in the search. // to cause a direct connection to be attempted at that point in the search.
// Any proxies listed after "direct" are never consulted. // Any proxies listed after "direct" are never consulted.
// //
// The GONOPROXY environment variable is a comma-separated list of // The GOPRIVATE and GONOPROXY environment variables allow bypassing
// glob patterns (in the syntax of Go's path.Match) of module path prefixes // the proxy for selected modules. See 'go help module-private' for details.
// that should always be fetched directly, ignoring the GOPROXY setting.
// For example,
//
// GONOPROXY=*.corp.example.com,rsc.io/private
//
// forces a direct connection to download modules with path prefixes matching
// either pattern, including "git.corp.example.com/xyzzy", "rsc.io/private",
// and "rsc.io/private/quux".
//
// The 'go env -w' command (see 'go help env') can be used to set these variables
// for future go command invocations.
// //
// No matter the source of the modules, the go command checks downloads against // No matter the source of the modules, the go command checks downloads against
// known checksums, to detect unexpected changes in the content of any specific // known checksums, to detect unexpected changes in the content of any specific
// module version from one day to the next. This check first consults the current // module version from one day to the next. This check first consults the current
// module's go.sum file but falls back to the Go checksum database. // module's go.sum file but falls back to the Go checksum database, controlled by
// See 'go help module-auth' for details. // the GOSUMDB and GONOSUMDB environment variables. See 'go help module-auth'
// for details.
// //
// See 'go help goproxy' for details about the proxy protocol and also // See 'go help goproxy' for details about the proxy protocol and also
// the format of the cached downloaded packages. // the format of the cached downloaded packages.
@ -2634,6 +2639,137 @@
// are still ignored. // are still ignored.
// //
// //
// Module authentication using go.sum
//
// The go command tries to authenticate every downloaded module,
// checking that the bits downloaded for a specific module version today
// match bits downloaded yesterday. This ensures repeatable builds
// and detects introduction of unexpected changes, malicious or not.
//
// In each module's root, alongside go.mod, the go command maintains
// a file named go.sum containing the cryptographic checksums of the
// module's dependencies.
//
// The form of each line in go.sum is three fields:
//
// <module> <version>[/go.mod] <hash>
//
// Each known module version results in two lines in the go.sum file.
// The first line gives the hash of the module version's file tree.
// The second line appends "/go.mod" to the version and gives the hash
// of only the module version's (possibly synthesized) go.mod file.
// The go.mod-only hash allows downloading and authenticating a
// module version's go.mod file, which is needed to compute the
// dependency graph, without also downloading all the module's source code.
//
// The hash begins with an algorithm prefix of the form "h<N>:".
// The only defined algorithm prefix is "h1:", which uses SHA-256.
//
// Module authentication failures
//
// The go command maintains a cache of downloaded packages and computes
// and records the cryptographic checksum of each package at download time.
// In normal operation, the go command checks the main module's go.sum file
// against these precomputed checksums instead of recomputing them on
// each command invocation. The 'go mod verify' command checks that
// the cached copies of module downloads still match both their recorded
// checksums and the entries in go.sum.
//
// In day-to-day development, the checksum of a given module version
// should never change. Each time a dependency is used by a given main
// module, the go command checks its local cached copy, freshly
// downloaded or not, against the main module's go.sum. If the checksums
// don't match, the go command reports the mismatch as a security error
// and refuses to run the build. When this happens, proceed with caution:
// code changing unexpectedly means today's build will not match
// yesterday's, and the unexpected change may not be beneficial.
//
// If the go command reports a mismatch in go.sum, the downloaded code
// for the reported module version does not match the one used in a
// previous build of the main module. It is important at that point
// to find out what the right checksum should be, to decide whether
// go.sum is wrong or the downloaded code is wrong. Usually go.sum is right:
// you want to use the same code you used yesterday.
//
// If a downloaded module is not yet included in go.sum and it is a publicly
// available module, the go command consults the Go checksum database to fetch
// the expected go.sum lines. If the downloaded code does not match those
// lines, the go command reports the mismatch and exits. Note that the
// database is not consulted for module versions already listed in go.sum.
//
// If a go.sum mismatch is reported, it is always worth investigating why
// the code downloaded today differs from what was downloaded yesterday.
//
// The GOSUMDB environment variable identifies the name of checksum database
// to use and optionally its public key and URL, as in:
//
// GOSUMDB="sum.golang.org"
// GOSUMDB="sum.golang.org+<publickey>"
// GOSUMDB="sum.golang.org+<publickey> https://sum.golang.org"
//
// The go command knows the public key of sum.golang.org; use of any other
// database requires giving the public key explicitly. The URL defaults to
// "https://" followed by the database name.
//
// GOSUMDB defaults to "sum.golang.org", the Go checksum database run by Google.
// See https://sum.golang.org/privacy for the service's privacy policy.
//
// If GOSUMDB is set to "off", or if "go get" is invoked with the -insecure flag,
// the checksum database is not consulted, and all unrecognized modules are
// accepted, at the cost of giving up the security guarantee of verified repeatable
// downloads for all modules. A better way to bypass the checksum database
// for specific modules is to use the GOPRIVATE or GONOSUMDB environment
// variables. See 'go help module-private' for details.
//
// The 'go env -w' command (see 'go help env') can be used to set these variables
// for future go command invocations.
//
//
// Module configuration for non-public modules
//
// The go command defaults to downloading modules from the public Go module
// mirror at proxy.golang.org. It also defaults to validating downloaded modules,
// regardless of source, against the public Go checksum database at sum.golang.org.
// These defaults work well for publicly available source code.
//
// The GOPRIVATE environment variable controls which modules the go command
// considers to be private (not available publicly) and should therefore not use the
// proxy or checksum database. The variable is a comma-separated list of
// glob patterns (in the syntax of Go's path.Match) of module path prefixes.
// For example,
//
// GOPRIVATE=*.corp.example.com,rsc.io/private
//
// causes the go command to treat as private any module with a path prefix
// matching either pattern, including git.corp.example.com/xyzzy, rsc.io/private,
// and rsc.io/private/quux.
//
// The GOPRIVATE environment variable may be used by other tools as well to
// identify non-public modules. For example, an editor could use GOPRIVATE
// to decide whether to hyperlink a package import to a godoc.org page.
//
// For fine-grained control over module download and validation, the GONOPROXY
// and GONOSUMDB environment variables accept the same kind of glob list
// and override GOPRIVATE for the specific decision of whether to use the proxy
// and checksum database, respectively.
//
// For example, if a company ran a module proxy serving private modules,
// users would configure go using:
//
// GOPRIVATE=*.corp.example.com
// GOPROXY=proxy.example.com
// GONOPROXY=none
//
// This would tell the go comamnd and other tools that modules beginning with
// a corp.example.com subdomain are private but that the company proxy should
// be used for downloading both public and private modules, because
// GONOPROXY has been set to a pattern that won't match any modules,
// overriding GOPRIVATE.
//
// The 'go env -w' command (see 'go help env') can be used to set these variables
// for future go command invocations.
//
//
// Package lists and patterns // Package lists and patterns
// //
// Many commands apply to a set of packages: // Many commands apply to a set of packages:
@ -2718,102 +2854,6 @@
// by the go tool, as are directories named "testdata". // by the go tool, as are directories named "testdata".
// //
// //
// Module authentication using go.sum
//
// The go command tries to authenticate every downloaded module,
// checking that the bits downloaded for a specific module version today
// match bits downloaded yesterday. This ensures repeatable builds
// and detects introduction of unexpected changes, malicious or not.
//
// In each module's root, alongside go.mod, the go command maintains
// a file named go.sum containing the cryptographic checksums of the
// module's dependencies.
//
// The form of each line in go.sum is three fields:
//
// <module> <version>[/go.mod] <hash>
//
// Each known module version results in two lines in the go.sum file.
// The first line gives the hash of the module version's file tree.
// The second line appends "/go.mod" to the version and gives the hash
// of only the module version's (possibly synthesized) go.mod file.
// The go.mod-only hash allows downloading and authenticating a
// module version's go.mod file, which is needed to compute the
// dependency graph, without also downloading all the module's source code.
//
// The hash begins with an algorithm prefix of the form "h<N>:".
// The only defined algorithm prefix is "h1:", which uses SHA-256.
//
// Module authentication failures
//
// The go command maintains a cache of downloaded packages and computes
// and records the cryptographic checksum of each package at download time.
// In normal operation, the go command checks the main module's go.sum file
// against these precomputed checksums instead of recomputing them on
// each command invocation. The 'go mod verify' command checks that
// the cached copies of module downloads still match both their recorded
// checksums and the entries in go.sum.
//
// In day-to-day development, the checksum of a given module version
// should never change. Each time a dependency is used by a given main
// module, the go command checks its local cached copy, freshly
// downloaded or not, against the main module's go.sum. If the checksums
// don't match, the go command reports the mismatch as a security error
// and refuses to run the build. When this happens, proceed with caution:
// code changing unexpectedly means today's build will not match
// yesterday's, and the unexpected change may not be beneficial.
//
// If the go command reports a mismatch in go.sum, the downloaded code
// for the reported module version does not match the one used in a
// previous build of the main module. It is important at that point
// to find out what the right checksum should be, to decide whether
// go.sum is wrong or the downloaded code is wrong. Usually go.sum is right:
// you want to use the same code you used yesterday.
//
// If a downloaded module is not yet included in go.sum and it is a publicly
// available module, the go command consults the Go checksum database to fetch
// the expected go.sum lines. If the downloaded code does not match those
// lines, the go command reports the mismatch and exits. Note that the
// database is not consulted for module versions already listed in go.sum.
//
// If a go.sum mismatch is reported, it is always worth investigating why
// the code downloaded today differs from what was downloaded yesterday.
//
// The GOSUMDB environment variable identifies the name of checksum database
// to use and optionally its public key and URL, as in:
//
// GOSUMDB="sum.golang.org"
// GOSUMDB="sum.golang.org+<publickey>"
// GOSUMDB="sum.golang.org+<publickey> https://sum.golang.org"
//
// The go command knows the public key of sum.golang.org; use of any other
// database requires giving the public key explicitly. The URL defaults to
// "https://" followed by the database name.
//
// GOSUMDB defaults to "sum.golang.org", the Go checksum database run by Google.
// See https://sum.golang.org/privacy for the service's privacy policy.
//
// If GOSUMDB is set to "off", or if "go get" is invoked with the -insecure flag,
// the checksum database is not consulted, and all unrecognized modules are
// accepted, at the cost of giving up the security guarantee of verified repeatable
// downloads for all modules. A better way to bypass the checksum database
// for specific modules is to use the GONOSUMDB environment variable.
//
// The GONOSUMDB environment variable is a comma-separated list of
// glob patterns (in the syntax of Go's path.Match) of module path prefixes
// that should not be compared against the checksum database.
// For example,
//
// GONOSUMDB=*.corp.example.com,rsc.io/private
//
// disables checksum database lookups for modules with path prefixes matching
// either pattern, including "git.corp.example.com/xyzzy", "rsc.io/private",
// and "rsc.io/private/quux".
//
// The 'go env -w' command (see 'go help env') can be used to set these variables
// for future go command invocations.
//
//
// Testing flags // Testing flags
// //
// The 'go test' command takes both flags that apply to 'go test' itself // The 'go test' command takes both flags that apply to 'go test' itself

View file

@ -29,6 +29,7 @@ import (
"cmd/go/internal/cache" "cmd/go/internal/cache"
"cmd/go/internal/cfg" "cmd/go/internal/cfg"
"cmd/go/internal/robustio"
"cmd/internal/sys" "cmd/internal/sys"
) )
@ -685,7 +686,7 @@ func (tg *testgoData) creatingTemp(path string) {
if tg.wd != "" && !filepath.IsAbs(path) { if tg.wd != "" && !filepath.IsAbs(path) {
path = filepath.Join(tg.pwd(), path) path = filepath.Join(tg.pwd(), path)
} }
tg.must(os.RemoveAll(path)) tg.must(robustio.RemoveAll(path))
tg.temps = append(tg.temps, path) tg.temps = append(tg.temps, path)
} }
@ -887,7 +888,7 @@ func removeAll(dir string) error {
} }
return nil return nil
}) })
return os.RemoveAll(dir) return robustio.RemoveAll(dir)
} }
// failSSH puts an ssh executable in the PATH that always fails. // failSSH puts an ssh executable in the PATH that always fails.
@ -1181,7 +1182,7 @@ func testMove(t *testing.T, vcs, url, base, config string) {
case "svn": case "svn":
// SVN doesn't believe in text files so we can't just edit the config. // SVN doesn't believe in text files so we can't just edit the config.
// Check out a different repo into the wrong place. // Check out a different repo into the wrong place.
tg.must(os.RemoveAll(tg.path("src/code.google.com/p/rsc-svn"))) tg.must(robustio.RemoveAll(tg.path("src/code.google.com/p/rsc-svn")))
tg.run("get", "-d", "-u", "code.google.com/p/rsc-svn2/trunk") tg.run("get", "-d", "-u", "code.google.com/p/rsc-svn2/trunk")
tg.must(os.Rename(tg.path("src/code.google.com/p/rsc-svn2"), tg.path("src/code.google.com/p/rsc-svn"))) tg.must(os.Rename(tg.path("src/code.google.com/p/rsc-svn2"), tg.path("src/code.google.com/p/rsc-svn")))
default: default:
@ -1222,10 +1223,12 @@ func TestInternalCache(t *testing.T) {
} }
func TestMoveGit(t *testing.T) { func TestMoveGit(t *testing.T) {
testenv.MustHaveExecPath(t, "git")
testMove(t, "git", "rsc.io/pdf", "pdf", "rsc.io/pdf/.git/config") testMove(t, "git", "rsc.io/pdf", "pdf", "rsc.io/pdf/.git/config")
} }
func TestMoveHG(t *testing.T) { func TestMoveHG(t *testing.T) {
testenv.MustHaveExecPath(t, "hg")
testMove(t, "hg", "vcs-test.golang.org/go/custom-hg-hello", "custom-hg-hello", "vcs-test.golang.org/go/custom-hg-hello/.hg/hgrc") testMove(t, "hg", "vcs-test.golang.org/go/custom-hg-hello", "custom-hg-hello", "vcs-test.golang.org/go/custom-hg-hello/.hg/hgrc")
} }
@ -1287,9 +1290,7 @@ func TestImportCycle(t *testing.T) {
// cmd/go: custom import path checking should not apply to Go packages without import comment. // cmd/go: custom import path checking should not apply to Go packages without import comment.
func TestIssue10952(t *testing.T) { func TestIssue10952(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
if _, err := exec.LookPath("git"); err != nil { testenv.MustHaveExecPath(t, "git")
t.Skip("skipping because git binary not found")
}
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -1305,9 +1306,7 @@ func TestIssue10952(t *testing.T) {
func TestIssue16471(t *testing.T) { func TestIssue16471(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
if _, err := exec.LookPath("git"); err != nil { testenv.MustHaveExecPath(t, "git")
t.Skip("skipping because git binary not found")
}
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -1323,9 +1322,7 @@ func TestIssue16471(t *testing.T) {
// Test git clone URL that uses SCP-like syntax and custom import path checking. // Test git clone URL that uses SCP-like syntax and custom import path checking.
func TestIssue11457(t *testing.T) { func TestIssue11457(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
if _, err := exec.LookPath("git"); err != nil { testenv.MustHaveExecPath(t, "git")
t.Skip("skipping because git binary not found")
}
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -1350,9 +1347,7 @@ func TestIssue11457(t *testing.T) {
func TestGetGitDefaultBranch(t *testing.T) { func TestGetGitDefaultBranch(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
if _, err := exec.LookPath("git"); err != nil { testenv.MustHaveExecPath(t, "git")
t.Skip("skipping because git binary not found")
}
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -1378,9 +1373,7 @@ func TestGetGitDefaultBranch(t *testing.T) {
// Security issue. Don't disable. See golang.org/issue/22125. // Security issue. Don't disable. See golang.org/issue/22125.
func TestAccidentalGitCheckout(t *testing.T) { func TestAccidentalGitCheckout(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
if _, err := exec.LookPath("git"); err != nil { testenv.MustHaveExecPath(t, "git")
t.Skip("skipping because git binary not found")
}
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -1653,6 +1646,7 @@ func TestInstallToGOBINCommandLinePackage(t *testing.T) {
func TestGoGetNonPkg(t *testing.T) { func TestGoGetNonPkg(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -1669,6 +1663,7 @@ func TestGoGetNonPkg(t *testing.T) {
func TestGoGetTestOnlyPkg(t *testing.T) { func TestGoGetTestOnlyPkg(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -1699,7 +1694,7 @@ func TestInstalls(t *testing.T) {
goarch := strings.TrimSpace(tg.getStdout()) goarch := strings.TrimSpace(tg.getStdout())
tg.setenv("GOARCH", goarch) tg.setenv("GOARCH", goarch)
fixbin := filepath.Join(goroot, "pkg", "tool", goos+"_"+goarch, "fix") + exeSuffix fixbin := filepath.Join(goroot, "pkg", "tool", goos+"_"+goarch, "fix") + exeSuffix
tg.must(os.RemoveAll(fixbin)) tg.must(robustio.RemoveAll(fixbin))
tg.run("install", "cmd/fix") tg.run("install", "cmd/fix")
tg.wantExecutable(fixbin, "did not install cmd/fix to $GOROOT/pkg/tool") tg.wantExecutable(fixbin, "did not install cmd/fix to $GOROOT/pkg/tool")
tg.must(os.Remove(fixbin)) tg.must(os.Remove(fixbin))
@ -2058,6 +2053,7 @@ func TestDefaultGOPATH(t *testing.T) {
func TestDefaultGOPATHGet(t *testing.T) { func TestDefaultGOPATHGet(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -2070,13 +2066,13 @@ func TestDefaultGOPATHGet(t *testing.T) {
tg.grepStderr("created GOPATH="+regexp.QuoteMeta(tg.path("home/go"))+"; see 'go help gopath'", "did not create GOPATH") tg.grepStderr("created GOPATH="+regexp.QuoteMeta(tg.path("home/go"))+"; see 'go help gopath'", "did not create GOPATH")
// no warning if directory already exists // no warning if directory already exists
tg.must(os.RemoveAll(tg.path("home/go"))) tg.must(robustio.RemoveAll(tg.path("home/go")))
tg.tempDir("home/go") tg.tempDir("home/go")
tg.run("get", "github.com/golang/example/hello") tg.run("get", "github.com/golang/example/hello")
tg.grepStderrNot(".", "expected no output on standard error") tg.grepStderrNot(".", "expected no output on standard error")
// error if $HOME/go is a file // error if $HOME/go is a file
tg.must(os.RemoveAll(tg.path("home/go"))) tg.must(robustio.RemoveAll(tg.path("home/go")))
tg.tempFile("home/go", "") tg.tempFile("home/go", "")
tg.runFail("get", "github.com/golang/example/hello") tg.runFail("get", "github.com/golang/example/hello")
tg.grepStderr(`mkdir .*[/\\]go: .*(not a directory|cannot find the path)`, "expected error because $HOME/go is a file") tg.grepStderr(`mkdir .*[/\\]go: .*(not a directory|cannot find the path)`, "expected error because $HOME/go is a file")
@ -2439,6 +2435,7 @@ func TestSymlinkWarning(t *testing.T) {
// Issue 8181. // Issue 8181.
func TestGoGetDashTIssue8181(t *testing.T) { func TestGoGetDashTIssue8181(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -2453,6 +2450,7 @@ func TestGoGetDashTIssue8181(t *testing.T) {
func TestIssue11307(t *testing.T) { func TestIssue11307(t *testing.T) {
// go get -u was not working except in checkout directory // go get -u was not working except in checkout directory
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -2875,7 +2873,7 @@ func TestCgoDependsOnSyscall(t *testing.T) {
files, err := filepath.Glob(filepath.Join(runtime.GOROOT(), "pkg", "*_race")) files, err := filepath.Glob(filepath.Join(runtime.GOROOT(), "pkg", "*_race"))
tg.must(err) tg.must(err)
for _, file := range files { for _, file := range files {
tg.check(os.RemoveAll(file)) tg.check(robustio.RemoveAll(file))
} }
tg.tempFile("src/foo/foo.go", ` tg.tempFile("src/foo/foo.go", `
package foo package foo
@ -2931,6 +2929,7 @@ func TestCgoPkgConfig(t *testing.T) {
tg.run("env", "PKG_CONFIG") tg.run("env", "PKG_CONFIG")
pkgConfig := strings.TrimSpace(tg.getStdout()) pkgConfig := strings.TrimSpace(tg.getStdout())
testenv.MustHaveExecPath(t, pkgConfig)
if out, err := exec.Command(pkgConfig, "--atleast-pkgconfig-version", "0.24").CombinedOutput(); err != nil { if out, err := exec.Command(pkgConfig, "--atleast-pkgconfig-version", "0.24").CombinedOutput(); err != nil {
t.Skipf("%s --atleast-pkgconfig-version 0.24: %v\n%s", pkgConfig, err, out) t.Skipf("%s --atleast-pkgconfig-version 0.24: %v\n%s", pkgConfig, err, out)
} }
@ -3033,9 +3032,7 @@ func TestIssue7573(t *testing.T) {
if !canCgo { if !canCgo {
t.Skip("skipping because cgo not enabled") t.Skip("skipping because cgo not enabled")
} }
if _, err := exec.LookPath("gccgo"); err != nil { testenv.MustHaveExecPath(t, "gccgo")
t.Skip("skipping because no gccgo compiler found")
}
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -3324,6 +3321,7 @@ func TestGoGenerateBadImports(t *testing.T) {
func TestGoGetCustomDomainWildcard(t *testing.T) { func TestGoGetCustomDomainWildcard(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -3335,6 +3333,7 @@ func TestGoGetCustomDomainWildcard(t *testing.T) {
func TestGoGetInternalWildcard(t *testing.T) { func TestGoGetInternalWildcard(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -3407,6 +3406,7 @@ func TestVetWithOnlyCgoFiles(t *testing.T) {
// Issue 9767, 19769. // Issue 9767, 19769.
func TestGoGetDotSlashDownload(t *testing.T) { func TestGoGetDotSlashDownload(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -3662,6 +3662,7 @@ func TestImportLocal(t *testing.T) {
func TestGoGetInsecure(t *testing.T) { func TestGoGetInsecure(t *testing.T) {
test := func(t *testing.T, modules bool) { test := func(t *testing.T, modules bool) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -3702,6 +3703,7 @@ func TestGoGetInsecure(t *testing.T) {
func TestGoGetUpdateInsecure(t *testing.T) { func TestGoGetUpdateInsecure(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -3726,6 +3728,7 @@ func TestGoGetUpdateInsecure(t *testing.T) {
func TestGoGetUpdateUnknownProtocol(t *testing.T) { func TestGoGetUpdateUnknownProtocol(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -3760,6 +3763,7 @@ func TestGoGetUpdateUnknownProtocol(t *testing.T) {
func TestGoGetInsecureCustomDomain(t *testing.T) { func TestGoGetInsecureCustomDomain(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -3862,6 +3866,7 @@ func TestGoGetUpdate(t *testing.T) {
// former dependencies, not current ones. // former dependencies, not current ones.
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -3889,6 +3894,7 @@ func TestGoGetUpdate(t *testing.T) {
// Issue #20512. // Issue #20512.
func TestGoGetRace(t *testing.T) { func TestGoGetRace(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
if !canRace { if !canRace {
t.Skip("skipping because race detector not supported") t.Skip("skipping because race detector not supported")
} }
@ -3905,6 +3911,7 @@ func TestGoGetDomainRoot(t *testing.T) {
// go get foo.io (not foo.io/subdir) was not working consistently. // go get foo.io (not foo.io/subdir) was not working consistently.
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -3919,10 +3926,10 @@ func TestGoGetDomainRoot(t *testing.T) {
tg.run("get", "go-get-issue-9357.appspot.com") tg.run("get", "go-get-issue-9357.appspot.com")
tg.run("get", "-u", "go-get-issue-9357.appspot.com") tg.run("get", "-u", "go-get-issue-9357.appspot.com")
tg.must(os.RemoveAll(tg.path("src/go-get-issue-9357.appspot.com"))) tg.must(robustio.RemoveAll(tg.path("src/go-get-issue-9357.appspot.com")))
tg.run("get", "go-get-issue-9357.appspot.com") tg.run("get", "go-get-issue-9357.appspot.com")
tg.must(os.RemoveAll(tg.path("src/go-get-issue-9357.appspot.com"))) tg.must(robustio.RemoveAll(tg.path("src/go-get-issue-9357.appspot.com")))
tg.run("get", "-u", "go-get-issue-9357.appspot.com") tg.run("get", "-u", "go-get-issue-9357.appspot.com")
} }
@ -4303,6 +4310,7 @@ func TestGenerateUsesBuildContext(t *testing.T) {
// Issue 14450: go get -u .../ tried to import not downloaded package // Issue 14450: go get -u .../ tried to import not downloaded package
func TestGoGetUpdateWithWildcard(t *testing.T) { func TestGoGetUpdateWithWildcard(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -4514,8 +4522,9 @@ func TestLinkXImportPathEscape(t *testing.T) {
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
tg.parallel() tg.parallel()
tg.makeTempdir()
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata")) tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
exe := "./linkx" + exeSuffix exe := tg.path("linkx" + exeSuffix)
tg.creatingTemp(exe) tg.creatingTemp(exe)
tg.run("build", "-o", exe, "-ldflags", "-X=my.pkg.Text=linkXworked", "my.pkg/main") tg.run("build", "-o", exe, "-ldflags", "-X=my.pkg.Text=linkXworked", "my.pkg/main")
out, err := exec.Command(exe).CombinedOutput() out, err := exec.Command(exe).CombinedOutput()
@ -4751,7 +4760,7 @@ func TestExecutableGOROOT(t *testing.T) {
check(t, symGoTool, newRoot) check(t, symGoTool, newRoot)
}) })
tg.must(os.RemoveAll(tg.path("new/pkg"))) tg.must(robustio.RemoveAll(tg.path("new/pkg")))
// Binaries built in the new tree should report the // Binaries built in the new tree should report the
// new tree when they call runtime.GOROOT. // new tree when they call runtime.GOROOT.
@ -5051,9 +5060,8 @@ func TestExecBuildX(t *testing.T) {
t.Skip("skipping because cgo not enabled") t.Skip("skipping because cgo not enabled")
} }
if runtime.GOOS == "plan9" || runtime.GOOS == "windows" { testenv.MustHaveExecPath(t, "/usr/bin/env")
t.Skipf("skipping because unix shell is not supported on %s", runtime.GOOS) testenv.MustHaveExecPath(t, "bash")
}
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
@ -5103,7 +5111,7 @@ func TestExecBuildX(t *testing.T) {
if len(matches) == 0 { if len(matches) == 0 {
t.Fatal("no WORK directory") t.Fatal("no WORK directory")
} }
tg.must(os.RemoveAll(matches[1])) tg.must(robustio.RemoveAll(matches[1]))
} }
func TestParallelNumber(t *testing.T) { func TestParallelNumber(t *testing.T) {
@ -5134,9 +5142,10 @@ func TestUpxCompression(t *testing.T) {
t.Skipf("skipping upx test on %s/%s", runtime.GOOS, runtime.GOARCH) t.Skipf("skipping upx test on %s/%s", runtime.GOOS, runtime.GOARCH)
} }
testenv.MustHaveExecPath(t, "upx")
out, err := exec.Command("upx", "--version").CombinedOutput() out, err := exec.Command("upx", "--version").CombinedOutput()
if err != nil { if err != nil {
t.Skip("skipping because upx is not available") t.Fatalf("upx --version failed: %v", err)
} }
// upx --version prints `upx <version>` in the first line of output: // upx --version prints `upx <version>` in the first line of output:
@ -5145,13 +5154,13 @@ func TestUpxCompression(t *testing.T) {
re := regexp.MustCompile(`([[:digit:]]+)\.([[:digit:]]+)`) re := regexp.MustCompile(`([[:digit:]]+)\.([[:digit:]]+)`)
upxVersion := re.FindStringSubmatch(string(out)) upxVersion := re.FindStringSubmatch(string(out))
if len(upxVersion) != 3 { if len(upxVersion) != 3 {
t.Errorf("bad upx version string: %s", upxVersion) t.Fatalf("bad upx version string: %s", upxVersion)
} }
major, err1 := strconv.Atoi(upxVersion[1]) major, err1 := strconv.Atoi(upxVersion[1])
minor, err2 := strconv.Atoi(upxVersion[2]) minor, err2 := strconv.Atoi(upxVersion[2])
if err1 != nil || err2 != nil { if err1 != nil || err2 != nil {
t.Errorf("bad upx version string: %s", upxVersion[0]) t.Fatalf("bad upx version string: %s", upxVersion[0])
} }
// Anything below 3.94 is known not to work with go binaries // Anything below 3.94 is known not to work with go binaries
@ -5204,26 +5213,29 @@ func TestQEMUUserMode(t *testing.T) {
src, obj := tg.path("main.go"), tg.path("main") src, obj := tg.path("main.go"), tg.path("main")
for _, arch := range testArchs { for _, arch := range testArchs {
out, err := exec.Command("qemu-"+arch.qemu, "--version").CombinedOutput() arch := arch
if err != nil { t.Run(arch.g, func(t *testing.T) {
t.Logf("Skipping %s test (qemu-%s not available)", arch.g, arch.qemu) qemu := "qemu-" + arch.qemu
continue testenv.MustHaveExecPath(t, qemu)
}
tg.setenv("GOARCH", arch.g) out, err := exec.Command(qemu, "--version").CombinedOutput()
tg.run("build", "-o", obj, src) if err != nil {
t.Fatalf("%s --version failed: %v", qemu, err)
}
out, err = exec.Command("qemu-"+arch.qemu, obj).CombinedOutput() tg.setenv("GOARCH", arch.g)
if err != nil { tg.run("build", "-o", obj, src)
t.Logf("qemu-%s output:\n%s\n", arch.qemu, out)
t.Errorf("qemu-%s failed with %v", arch.qemu, err) out, err = exec.Command(qemu, obj).CombinedOutput()
continue if err != nil {
} t.Logf("%s output:\n%s\n", qemu, out)
if want := "hello qemu-user"; string(out) != want { t.Fatalf("%s failed with %v", qemu, err)
t.Errorf("bad output from qemu-%s:\ngot %s; want %s", arch.qemu, out, want) }
} if want := "hello qemu-user"; string(out) != want {
t.Errorf("bad output from %s:\ngot %s; want %s", qemu, out, want)
}
})
} }
} }
func TestCacheListStale(t *testing.T) { func TestCacheListStale(t *testing.T) {

View file

@ -12,6 +12,8 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"testing" "testing"
"cmd/go/internal/robustio"
) )
func TestAbsolutePath(t *testing.T) { func TestAbsolutePath(t *testing.T) {
@ -21,7 +23,7 @@ func TestAbsolutePath(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer os.RemoveAll(tmp) defer robustio.RemoveAll(tmp)
file := filepath.Join(tmp, "a.go") file := filepath.Join(tmp, "a.go")
err = ioutil.WriteFile(file, []byte{}, 0644) err = ioutil.WriteFile(file, []byte{}, 0644)

View file

@ -261,7 +261,7 @@ func (c *Cache) Trim() {
// We maintain in dir/trim.txt the time of the last completed cache trim. // We maintain in dir/trim.txt the time of the last completed cache trim.
// If the cache has been trimmed recently enough, do nothing. // If the cache has been trimmed recently enough, do nothing.
// This is the common case. // This is the common case.
data, _ := ioutil.ReadFile(filepath.Join(c.dir, "trim.txt")) data, _ := renameio.ReadFile(filepath.Join(c.dir, "trim.txt"))
t, err := strconv.ParseInt(strings.TrimSpace(string(data)), 10, 64) t, err := strconv.ParseInt(strings.TrimSpace(string(data)), 10, 64)
if err == nil && now.Sub(time.Unix(t, 0)) < trimInterval { if err == nil && now.Sub(time.Unix(t, 0)) < trimInterval {
return return

View file

@ -265,6 +265,7 @@ var knownEnv = `
GOOS GOOS
GOPATH GOPATH
GOPPC64 GOPPC64
GOPRIVATE
GOPROXY GOPROXY
GOROOT GOROOT
GOSUMDB GOSUMDB
@ -291,30 +292,13 @@ var (
GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", objabi.GOPPC64)) GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", objabi.GOPPC64))
GOWASM = envOr("GOWASM", fmt.Sprint(objabi.GOWASM)) GOWASM = envOr("GOWASM", fmt.Sprint(objabi.GOWASM))
GOPROXY = goproxy() GOPROXY = envOr("GOPROXY", "https://proxy.golang.org,direct")
GOSUMDB = gosumdb() GOSUMDB = envOr("GOSUMDB", "sum.golang.org")
GONOPROXY = Getenv("GONOPROXY") GOPRIVATE = Getenv("GOPRIVATE")
GONOSUMDB = Getenv("GONOSUMDB") GONOPROXY = envOr("GONOPROXY", GOPRIVATE)
GONOSUMDB = envOr("GONOSUMDB", GOPRIVATE)
) )
func goproxy() string {
v := Getenv("GOPROXY")
if v != "" {
return v
}
return "https://proxy.golang.org"
}
func gosumdb() string {
v := Getenv("GOSUMDB")
if v != "" {
return v
}
return "sum.golang.org"
}
// GetArchEnv returns the name and setting of the // GetArchEnv returns the name and setting of the
// GOARCH-specific architecture environment variable. // GOARCH-specific architecture environment variable.
// If the current architecture has no GOARCH-specific variable, // If the current architecture has no GOARCH-specific variable,

View file

@ -79,6 +79,7 @@ func MkEnv() []cfg.EnvVar {
{Name: "GONOSUMDB", Value: cfg.GONOSUMDB}, {Name: "GONOSUMDB", Value: cfg.GONOSUMDB},
{Name: "GOOS", Value: cfg.Goos}, {Name: "GOOS", Value: cfg.Goos},
{Name: "GOPATH", Value: cfg.BuildContext.GOPATH}, {Name: "GOPATH", Value: cfg.BuildContext.GOPATH},
{Name: "GOPRIVATE", Value: cfg.GOPRIVATE},
{Name: "GOPROXY", Value: cfg.GOPROXY}, {Name: "GOPROXY", Value: cfg.GOPROXY},
{Name: "GOROOT", Value: cfg.GOROOT}, {Name: "GOROOT", Value: cfg.GOROOT},
{Name: "GOSUMDB", Value: cfg.GOSUMDB}, {Name: "GOSUMDB", Value: cfg.GOSUMDB},

View file

@ -41,6 +41,9 @@ func checkPath(path string, fileName bool) error {
if path == "" { if path == "" {
return fmt.Errorf("empty string") return fmt.Errorf("empty string")
} }
if path[0] == '-' {
return fmt.Errorf("leading dash")
}
if strings.Contains(path, "..") { if strings.Contains(path, "..") {
return fmt.Errorf("double dot") return fmt.Errorf("double dot")
} }

View file

@ -112,7 +112,7 @@ var vcsHg = &vcsCmd{
name: "Mercurial", name: "Mercurial",
cmd: "hg", cmd: "hg",
createCmd: []string{"clone -U {repo} {dir}"}, createCmd: []string{"clone -U -- {repo} {dir}"},
downloadCmd: []string{"pull"}, downloadCmd: []string{"pull"},
// We allow both tag and branch names as 'tags' // We allow both tag and branch names as 'tags'
@ -128,7 +128,7 @@ var vcsHg = &vcsCmd{
tagSyncDefault: []string{"update default"}, tagSyncDefault: []string{"update default"},
scheme: []string{"https", "http", "ssh"}, scheme: []string{"https", "http", "ssh"},
pingCmd: "identify {scheme}://{repo}", pingCmd: "identify -- {scheme}://{repo}",
remoteRepo: hgRemoteRepo, remoteRepo: hgRemoteRepo,
} }
@ -145,7 +145,7 @@ var vcsGit = &vcsCmd{
name: "Git", name: "Git",
cmd: "git", cmd: "git",
createCmd: []string{"clone {repo} {dir}", "-go-internal-cd {dir} submodule update --init --recursive"}, createCmd: []string{"clone -- {repo} {dir}", "-go-internal-cd {dir} submodule update --init --recursive"},
downloadCmd: []string{"pull --ff-only", "submodule update --init --recursive"}, downloadCmd: []string{"pull --ff-only", "submodule update --init --recursive"},
tagCmd: []tagCmd{ tagCmd: []tagCmd{
@ -165,7 +165,7 @@ var vcsGit = &vcsCmd{
tagSyncDefault: []string{"submodule update --init --recursive"}, tagSyncDefault: []string{"submodule update --init --recursive"},
scheme: []string{"git", "https", "http", "git+ssh", "ssh"}, scheme: []string{"git", "https", "http", "git+ssh", "ssh"},
pingCmd: "ls-remote {scheme}://{repo}", pingCmd: "ls-remote -- {scheme}://{repo}",
remoteRepo: gitRemoteRepo, remoteRepo: gitRemoteRepo,
} }
@ -222,7 +222,7 @@ var vcsBzr = &vcsCmd{
name: "Bazaar", name: "Bazaar",
cmd: "bzr", cmd: "bzr",
createCmd: []string{"branch {repo} {dir}"}, createCmd: []string{"branch -- {repo} {dir}"},
// Without --overwrite bzr will not pull tags that changed. // Without --overwrite bzr will not pull tags that changed.
// Replace by --overwrite-tags after http://pad.lv/681792 goes in. // Replace by --overwrite-tags after http://pad.lv/681792 goes in.
@ -233,7 +233,7 @@ var vcsBzr = &vcsCmd{
tagSyncDefault: []string{"update -r revno:-1"}, tagSyncDefault: []string{"update -r revno:-1"},
scheme: []string{"https", "http", "bzr", "bzr+ssh"}, scheme: []string{"https", "http", "bzr", "bzr+ssh"},
pingCmd: "info {scheme}://{repo}", pingCmd: "info -- {scheme}://{repo}",
remoteRepo: bzrRemoteRepo, remoteRepo: bzrRemoteRepo,
resolveRepo: bzrResolveRepo, resolveRepo: bzrResolveRepo,
} }
@ -284,14 +284,14 @@ var vcsSvn = &vcsCmd{
name: "Subversion", name: "Subversion",
cmd: "svn", cmd: "svn",
createCmd: []string{"checkout {repo} {dir}"}, createCmd: []string{"checkout -- {repo} {dir}"},
downloadCmd: []string{"update"}, downloadCmd: []string{"update"},
// There is no tag command in subversion. // There is no tag command in subversion.
// The branch information is all in the path names. // The branch information is all in the path names.
scheme: []string{"https", "http", "svn", "svn+ssh"}, scheme: []string{"https", "http", "svn", "svn+ssh"},
pingCmd: "info {scheme}://{repo}", pingCmd: "info -- {scheme}://{repo}",
remoteRepo: svnRemoteRepo, remoteRepo: svnRemoteRepo,
} }
@ -334,7 +334,7 @@ var vcsFossil = &vcsCmd{
name: "Fossil", name: "Fossil",
cmd: "fossil", cmd: "fossil",
createCmd: []string{"-go-internal-mkdir {dir} clone {repo} " + filepath.Join("{dir}", fossilRepoName), "-go-internal-cd {dir} open .fossil"}, createCmd: []string{"-go-internal-mkdir {dir} clone -- {repo} " + filepath.Join("{dir}", fossilRepoName), "-go-internal-cd {dir} open .fossil"},
downloadCmd: []string{"up"}, downloadCmd: []string{"up"},
tagCmd: []tagCmd{{"tag ls", `(.*)`}}, tagCmd: []tagCmd{{"tag ls", `(.*)`}},
@ -856,6 +856,9 @@ func validateRepoRoot(repoRoot string) error {
if url.Scheme == "" { if url.Scheme == "" {
return errors.New("no scheme") return errors.New("no scheme")
} }
if url.Scheme == "file" {
return errors.New("file scheme disallowed")
}
return nil return nil
} }

View file

@ -509,9 +509,17 @@ General-purpose environment variables:
GOPATH GOPATH
For more details see: 'go help gopath'. For more details see: 'go help gopath'.
GOPROXY GOPROXY
URL of Go module proxy. See 'go help goproxy'. URL of Go module proxy. See 'go help modules'.
GOPRIVATE, GONOPROXY, GONOSUMDB
Comma-separated list of glob patterns (in the syntax of Go's path.Match)
of module path prefixes that should always be fetched directly
or that should not be compared against the checksum database.
See 'go help module-private'.
GOROOT GOROOT
The root of the go tree. The root of the go tree.
GOSUMDB
The name of checksum database to use and optionally its public key and
URL. See 'go help module-auth'.
GOTMPDIR GOTMPDIR
The directory where the go command will write The directory where the go command will write
temporary source files, packages, and binaries. temporary source files, packages, and binaries.

View file

@ -8,6 +8,9 @@ import "cmd/go/internal/cfg"
var tags map[string]bool var tags map[string]bool
// Tags returns a set of build tags that are true for the target platform.
// It includes GOOS, GOARCH, the compiler, possibly "cgo",
// release tags like "go1.13", and user-specified build tags.
func Tags() map[string]bool { func Tags() map[string]bool {
if tags == nil { if tags == nil {
tags = loadTags() tags = loadTags()
@ -32,3 +35,15 @@ func loadTags() map[string]bool {
} }
return tags return tags
} }
var anyTags map[string]bool
// AnyTags returns a special set of build tags that satisfy nearly all
// build tag expressions. Only "ignore" and malformed build tag requirements
// are considered false.
func AnyTags() map[string]bool {
if anyTags == nil {
anyTags = map[string]bool{"*": true}
}
return anyTags
}

View file

@ -6,32 +6,8 @@ package load
import ( import (
"path/filepath" "path/filepath"
"strings"
) )
// hasSubdir reports whether dir is a subdirectory of
// (possibly multiple levels below) root.
// If so, it sets rel to the path fragment that must be
// appended to root to reach dir.
func hasSubdir(root, dir string) (rel string, ok bool) {
if p, err := filepath.EvalSymlinks(root); err == nil {
root = p
}
if p, err := filepath.EvalSymlinks(dir); err == nil {
dir = p
}
const sep = string(filepath.Separator)
root = filepath.Clean(root)
if !strings.HasSuffix(root, sep) {
root += sep
}
dir = filepath.Clean(dir)
if !strings.HasPrefix(dir, root) {
return "", false
}
return filepath.ToSlash(dir[len(root):]), true
}
// expandPath returns the symlink-expanded form of path. // expandPath returns the symlink-expanded form of path.
func expandPath(p string) string { func expandPath(p string) string {
x, err := filepath.EvalSymlinks(p) x, err := filepath.EvalSymlinks(p)

View file

@ -5,6 +5,7 @@
package modcmd package modcmd
import ( import (
"cmd/go/internal/cfg"
"encoding/json" "encoding/json"
"os" "os"
@ -67,6 +68,13 @@ type moduleJSON struct {
} }
func runDownload(cmd *base.Command, args []string) { func runDownload(cmd *base.Command, args []string) {
// Check whether modules are enabled and whether we're in a module.
if cfg.Getenv("GO111MODULE") == "off" {
base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
}
if !modload.HasModRoot() && len(args) == 0 {
base.Fatalf("go mod download: no modules specified (see 'go help mod download')")
}
if len(args) == 0 { if len(args) == 0 {
args = []string{"all"} args = []string{"all"}
} }

View file

@ -8,6 +8,7 @@ package modcmd
import ( import (
"bufio" "bufio"
"cmd/go/internal/cfg"
"os" "os"
"sort" "sort"
@ -33,6 +34,14 @@ func runGraph(cmd *base.Command, args []string) {
if len(args) > 0 { if len(args) > 0 {
base.Fatalf("go mod graph: graph takes no arguments") base.Fatalf("go mod graph: graph takes no arguments")
} }
// Checks go mod expected behavior
if !modload.Enabled() {
if cfg.Getenv("GO111MODULE") == "off" {
base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
} else {
base.Fatalf("go: cannot find main module; see 'go help modules'")
}
}
modload.LoadBuildList() modload.LoadBuildList()
reqs := modload.MinReqs() reqs := modload.MinReqs()

View file

@ -166,8 +166,6 @@ func matchMetadata(dir string, info os.FileInfo) bool {
return false return false
} }
var anyTagsExceptIgnore = map[string]bool{"*": true}
// matchPotentialSourceFile reports whether info may be relevant to a build operation. // matchPotentialSourceFile reports whether info may be relevant to a build operation.
func matchPotentialSourceFile(dir string, info os.FileInfo) bool { func matchPotentialSourceFile(dir string, info os.FileInfo) bool {
if strings.HasSuffix(info.Name(), "_test.go") { if strings.HasSuffix(info.Name(), "_test.go") {
@ -181,7 +179,7 @@ func matchPotentialSourceFile(dir string, info os.FileInfo) bool {
defer f.Close() defer f.Close()
content, err := imports.ReadImports(f, false, nil) content, err := imports.ReadImports(f, false, nil)
if err == nil && !imports.ShouldBuild(content, anyTagsExceptIgnore) { if err == nil && !imports.ShouldBuild(content, imports.AnyTags()) {
// The file is explicitly tagged "ignore", so it can't affect the build. // The file is explicitly tagged "ignore", so it can't affect the build.
// Leave it out. // Leave it out.
return false return false

View file

@ -6,6 +6,7 @@ package modcmd
import ( import (
"bytes" "bytes"
"cmd/go/internal/cfg"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
@ -36,6 +37,14 @@ func runVerify(cmd *base.Command, args []string) {
// NOTE(rsc): Could take a module pattern. // NOTE(rsc): Could take a module pattern.
base.Fatalf("go mod verify: verify takes no arguments") base.Fatalf("go mod verify: verify takes no arguments")
} }
// Checks go mod expected behavior
if !modload.Enabled() {
if cfg.Getenv("GO111MODULE") == "off" {
base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
} else {
base.Fatalf("go: cannot find main module; see 'go help modules'")
}
}
ok := true ok := true
for _, mod := range modload.LoadBuildList()[1:] { for _, mod := range modload.LoadBuildList()[1:] {
ok = verifyMod(mod) && ok ok = verifyMod(mod) && ok

View file

@ -28,7 +28,7 @@ func TestMain(m *testing.M) {
} }
func testMain(m *testing.M) int { func testMain(m *testing.M) int {
modfetch.SetProxy("direct") cfg.GOPROXY = "direct"
if _, err := exec.LookPath("git"); err != nil { if _, err := exec.LookPath("git"); err != nil {
fmt.Fprintln(os.Stderr, "skipping because git binary not found") fmt.Fprintln(os.Stderr, "skipping because git binary not found")
@ -106,11 +106,11 @@ func TestConvertLegacyConfig(t *testing.T) {
github.com/AdRoll/goamz v0.0.0-20150130162828-d3664b76d905 github.com/AdRoll/goamz v0.0.0-20150130162828-d3664b76d905
github.com/MSOpenTech/azure-sdk-for-go v0.0.0-20150323223030-d90753bcad2e github.com/MSOpenTech/azure-sdk-for-go v0.0.0-20150323223030-d90753bcad2e
github.com/Sirupsen/logrus v0.7.3 github.com/Sirupsen/logrus v0.7.3
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd github.com/bugsnag/bugsnag-go v1.0.3-0.20141110184014-b1d153021fcd
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b
github.com/bugsnag/panicwrap v0.0.0-20141110184334-e5f9854865b9 github.com/bugsnag/panicwrap v0.0.0-20141110184334-e5f9854865b9
github.com/codegangsta/cli v0.0.0-20150131031259-6086d7927ec3 github.com/codegangsta/cli v1.4.2-0.20150131031259-6086d7927ec3
github.com/docker/docker v0.0.0-20150204013315-165ea5c158cf github.com/docker/docker v1.4.2-0.20150204013315-165ea5c158cf
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7
github.com/gorilla/context v0.0.0-20140604161150-14f550f51af5 github.com/gorilla/context v0.0.0-20140604161150-14f550f51af5
@ -118,7 +118,7 @@ func TestConvertLegacyConfig(t *testing.T) {
github.com/gorilla/mux v0.0.0-20140926153814-e444e69cbd2e github.com/gorilla/mux v0.0.0-20140926153814-e444e69cbd2e
github.com/jlhawn/go-crypto v0.0.0-20150401213827-cd738dde20f0 github.com/jlhawn/go-crypto v0.0.0-20150401213827-cd738dde20f0
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 github.com/yvasiyarov/gorelic v0.0.7-0.20141212073537-a9bba5b9ab50
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f
golang.org/x/net v0.0.0-20150202051010-1dfe7915deaf golang.org/x/net v0.0.0-20150202051010-1dfe7915deaf
gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789 gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789
@ -138,7 +138,7 @@ func TestConvertLegacyConfig(t *testing.T) {
github.com/googleapis/gax-go v2.0.0+incompatible github.com/googleapis/gax-go v2.0.0+incompatible
golang.org/x/net v0.0.0-20180216171745-136a25c244d3 golang.org/x/net v0.0.0-20180216171745-136a25c244d3
golang.org/x/oauth2 v0.0.0-20180207181906-543e37812f10 golang.org/x/oauth2 v0.0.0-20180207181906-543e37812f10
golang.org/x/text v0.0.0-20180208041248-4e4a3210bb54 golang.org/x/text v0.3.1-0.20180208041248-4e4a3210bb54
google.golang.org/api v0.0.0-20180217000815-c7a403bb5fe1 google.golang.org/api v0.0.0-20180217000815-c7a403bb5fe1
google.golang.org/appengine v1.0.0 google.golang.org/appengine v1.0.0
google.golang.org/genproto v0.0.0-20180206005123-2b5a72b8730b google.golang.org/genproto v0.0.0-20180206005123-2b5a72b8730b

View file

@ -6,9 +6,9 @@ package modconv
import ( import (
"fmt" "fmt"
"internal/lazyregexp"
"net/url" "net/url"
"path" "path"
"regexp"
"strconv" "strconv"
"strings" "strings"
@ -96,7 +96,7 @@ func ParseGopkgLock(file string, data []byte) (*modfile.File, error) {
return mf, nil return mf, nil
} }
var scpSyntaxReg = regexp.MustCompile(`^([a-zA-Z0-9_]+)@([a-zA-Z0-9._-]+):(.*)$`) var scpSyntaxReg = lazyregexp.New(`^([a-zA-Z0-9_]+)@([a-zA-Z0-9._-]+):(.*)$`)
func decodeSource(source string) (string, error) { func decodeSource(source string) (string, error) {
var u *url.URL var u *url.URL

View file

@ -15,6 +15,7 @@ import (
"strings" "strings"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/lockedfile" "cmd/go/internal/lockedfile"
"cmd/go/internal/modfetch/codehost" "cmd/go/internal/modfetch/codehost"
"cmd/go/internal/module" "cmd/go/internal/module"
@ -258,12 +259,12 @@ func (r *cachingRepo) Zip(dst io.Writer, version string) error {
// Stat is like Lookup(path).Stat(rev) but avoids the // Stat is like Lookup(path).Stat(rev) but avoids the
// repository path resolution in Lookup if the result is // repository path resolution in Lookup if the result is
// already cached on local disk. // already cached on local disk.
func Stat(path, rev string) (*RevInfo, error) { func Stat(proxy, path, rev string) (*RevInfo, error) {
_, info, err := readDiskStat(path, rev) _, info, err := readDiskStat(path, rev)
if err == nil { if err == nil {
return info, nil return info, nil
} }
repo, err := Lookup(path) repo, err := Lookup(proxy, path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -276,9 +277,22 @@ func InfoFile(path, version string) (string, error) {
if !semver.IsValid(version) { if !semver.IsValid(version) {
return "", fmt.Errorf("invalid version %q", version) return "", fmt.Errorf("invalid version %q", version)
} }
if _, err := Stat(path, version); err != nil {
if file, _, err := readDiskStat(path, version); err == nil {
return file, nil
}
err := TryProxies(func(proxy string) error {
repo, err := Lookup(proxy, path)
if err == nil {
_, err = repo.Stat(version)
}
return err
})
if err != nil {
return "", err return "", err
} }
// Stat should have populated the disk cache for us. // Stat should have populated the disk cache for us.
file, _, err := readDiskStat(path, version) file, _, err := readDiskStat(path, version)
if err != nil { if err != nil {
@ -294,21 +308,39 @@ func GoMod(path, rev string) ([]byte, error) {
// Convert commit hash to pseudo-version // Convert commit hash to pseudo-version
// to increase cache hit rate. // to increase cache hit rate.
if !semver.IsValid(rev) { if !semver.IsValid(rev) {
info, err := Stat(path, rev) if _, info, err := readDiskStat(path, rev); err == nil {
if err != nil { rev = info.Version
return nil, err } else {
err := TryProxies(func(proxy string) error {
repo, err := Lookup(proxy, path)
if err != nil {
return err
}
info, err := repo.Stat(rev)
if err == nil {
rev = info.Version
}
return err
})
if err != nil {
return nil, err
}
} }
rev = info.Version
} }
_, data, err := readDiskGoMod(path, rev) _, data, err := readDiskGoMod(path, rev)
if err == nil { if err == nil {
return data, nil return data, nil
} }
repo, err := Lookup(path)
if err != nil { err = TryProxies(func(proxy string) error {
return nil, err repo, err := Lookup(proxy, path)
} if err == nil {
return repo.GoMod(rev) data, err = repo.GoMod(rev)
}
return err
})
return data, err
} }
// GoModFile is like GoMod but returns the name of the file containing // GoModFile is like GoMod but returns the name of the file containing
@ -354,8 +386,29 @@ var errNotCached = fmt.Errorf("not in cache")
func readDiskStat(path, rev string) (file string, info *RevInfo, err error) { func readDiskStat(path, rev string) (file string, info *RevInfo, err error) {
file, data, err := readDiskCache(path, rev, "info") file, data, err := readDiskCache(path, rev, "info")
if err != nil { if err != nil {
if file, info, err := readDiskStatByHash(path, rev); err == nil { // If the cache already contains a pseudo-version with the given hash, we
return file, info, nil // would previously return that pseudo-version without checking upstream.
// However, that produced an unfortunate side-effect: if the author added a
// tag to the repository, 'go get' would not pick up the effect of that new
// tag on the existing commits, and 'go' commands that referred to those
// commits would use the previous name instead of the new one.
//
// That's especially problematic if the original pseudo-version starts with
// v0.0.0-, as was the case for all pseudo-versions during vgo development,
// since a v0.0.0- pseudo-version has lower precedence than pretty much any
// tagged version.
//
// In practice, we're only looking up by hash during initial conversion of a
// legacy config and during an explicit 'go get', and a little extra latency
// for those operations seems worth the benefit of picking up more accurate
// versions.
//
// Fall back to this resolution scheme only if the GOPROXY setting prohibits
// us from resolving upstream tags.
if cfg.GOPROXY == "off" {
if file, info, err := readDiskStatByHash(path, rev); err == nil {
return file, info, nil
}
} }
return file, nil, err return file, nil, err
} }
@ -405,13 +458,23 @@ func readDiskStatByHash(path, rev string) (file string, info *RevInfo, err error
if err != nil { if err != nil {
return "", nil, errNotCached return "", nil, errNotCached
} }
// A given commit hash may map to more than one pseudo-version,
// depending on which tags are present on the repository.
// Take the highest such version.
var maxVersion string
suffix := "-" + rev + ".info" suffix := "-" + rev + ".info"
err = errNotCached
for _, name := range names { for _, name := range names {
if strings.HasSuffix(name, suffix) && IsPseudoVersion(strings.TrimSuffix(name, ".info")) { if strings.HasSuffix(name, suffix) {
return readDiskStat(path, strings.TrimSuffix(name, ".info")) v := strings.TrimSuffix(name, ".info")
if IsPseudoVersion(v) && semver.Max(maxVersion, v) == v {
maxVersion = v
file, info, err = readDiskStat(path, strings.TrimSuffix(name, ".info"))
}
} }
} }
return "", nil, errNotCached return file, info, err
} }
// oldVgoPrefix is the prefix in the old auto-generated cached go.mod files. // oldVgoPrefix is the prefix in the old auto-generated cached go.mod files.
@ -451,7 +514,7 @@ func readDiskCache(path, rev, suffix string) (file string, data []byte, err erro
if err != nil { if err != nil {
return "", nil, errNotCached return "", nil, errNotCached
} }
data, err = ioutil.ReadFile(file) data, err = renameio.ReadFile(file)
if err != nil { if err != nil {
return file, nil, errNotCached return file, nil, errNotCached
} }
@ -545,7 +608,7 @@ func rewriteVersionList(dir string) {
buf.WriteString(v) buf.WriteString(v)
buf.WriteString("\n") buf.WriteString("\n")
} }
old, _ := ioutil.ReadFile(listFile) old, _ := renameio.ReadFile(listFile)
if bytes.Equal(buf.Bytes(), old) { if bytes.Equal(buf.Bytes(), old) {
return return
} }

View file

@ -80,7 +80,7 @@ func newGitRepo(remote string, localOK bool) (Repo, error) {
// but this lets us say git fetch origin instead, which // but this lets us say git fetch origin instead, which
// is a little nicer. More importantly, using a named remote // is a little nicer. More importantly, using a named remote
// avoids a problem with Git LFS. See golang.org/issue/25605. // avoids a problem with Git LFS. See golang.org/issue/25605.
if _, err := Run(r.dir, "git", "remote", "add", "origin", r.remote); err != nil { if _, err := Run(r.dir, "git", "remote", "add", "origin", "--", r.remote); err != nil {
os.RemoveAll(r.dir) os.RemoveAll(r.dir)
return nil, err return nil, err
} }
@ -123,8 +123,10 @@ type gitRepo struct {
statCache par.Cache statCache par.Cache
refsOnce sync.Once refsOnce sync.Once
refs map[string]string // refs maps branch and tag refs (e.g., "HEAD", "refs/heads/master")
refsErr error // to commits (e.g., "37ffd2e798afde829a34e8955b716ab730b2a6d6")
refs map[string]string
refsErr error
localTagsOnce sync.Once localTagsOnce sync.Once
localTags map[string]bool localTags map[string]bool
@ -407,7 +409,7 @@ func (r *gitRepo) fetchUnshallow(refSpecs ...string) error {
// statLocal returns a RevInfo describing rev in the local git repository. // statLocal returns a RevInfo describing rev in the local git repository.
// It uses version as info.Version. // It uses version as info.Version.
func (r *gitRepo) statLocal(version, rev string) (*RevInfo, error) { func (r *gitRepo) statLocal(version, rev string) (*RevInfo, error) {
out, err := Run(r.dir, "git", "-c", "log.showsignature=false", "log", "-n1", "--format=format:%H %ct %D", rev) out, err := Run(r.dir, "git", "-c", "log.showsignature=false", "log", "-n1", "--format=format:%H %ct %D", rev, "--")
if err != nil { if err != nil {
return nil, fmt.Errorf("unknown revision %s", rev) return nil, fmt.Errorf("unknown revision %s", rev)
} }

View file

@ -143,7 +143,7 @@ var vcsCmds = map[string]*vcsCmd{
"hg": { "hg": {
vcs: "hg", vcs: "hg",
init: func(remote string) []string { init: func(remote string) []string {
return []string{"hg", "clone", "-U", remote, "."} return []string{"hg", "clone", "-U", "--", remote, "."}
}, },
tags: func(remote string) []string { tags: func(remote string) []string {
return []string{"hg", "tags", "-q"} return []string{"hg", "tags", "-q"}
@ -168,7 +168,7 @@ var vcsCmds = map[string]*vcsCmd{
if subdir != "" { if subdir != "" {
pattern = []string{"-I", subdir + "/**"} pattern = []string{"-I", subdir + "/**"}
} }
return str.StringList("hg", "archive", "-t", "zip", "--no-decode", "-r", rev, "--prefix=prefix/", pattern, target) return str.StringList("hg", "archive", "-t", "zip", "--no-decode", "-r", rev, "--prefix=prefix/", pattern, "--", target)
}, },
}, },
@ -176,7 +176,7 @@ var vcsCmds = map[string]*vcsCmd{
vcs: "svn", vcs: "svn",
init: nil, // no local checkout init: nil, // no local checkout
tags: func(remote string) []string { tags: func(remote string) []string {
return []string{"svn", "list", strings.TrimSuffix(remote, "/trunk") + "/tags"} return []string{"svn", "list", "--", strings.TrimSuffix(remote, "/trunk") + "/tags"}
}, },
tagRE: re(`(?m)^(.*?)/?$`), tagRE: re(`(?m)^(.*?)/?$`),
statLocal: func(rev, remote string) []string { statLocal: func(rev, remote string) []string {
@ -184,12 +184,12 @@ var vcsCmds = map[string]*vcsCmd{
if rev == "latest" { if rev == "latest" {
suffix = "" suffix = ""
} }
return []string{"svn", "log", "-l1", "--xml", remote + suffix} return []string{"svn", "log", "-l1", "--xml", "--", remote + suffix}
}, },
parseStat: svnParseStat, parseStat: svnParseStat,
latest: "latest", latest: "latest",
readFile: func(rev, file, remote string) []string { readFile: func(rev, file, remote string) []string {
return []string{"svn", "cat", remote + "/" + file + "@" + rev} return []string{"svn", "cat", "--", remote + "/" + file + "@" + rev}
}, },
// TODO: zip // TODO: zip
}, },
@ -197,7 +197,7 @@ var vcsCmds = map[string]*vcsCmd{
"bzr": { "bzr": {
vcs: "bzr", vcs: "bzr",
init: func(remote string) []string { init: func(remote string) []string {
return []string{"bzr", "branch", "--use-existing-dir", remote, "."} return []string{"bzr", "branch", "--use-existing-dir", "--", remote, "."}
}, },
fetch: []string{ fetch: []string{
"bzr", "pull", "--overwrite-tags", "bzr", "pull", "--overwrite-tags",
@ -220,14 +220,14 @@ var vcsCmds = map[string]*vcsCmd{
if subdir != "" { if subdir != "" {
extra = []string{"./" + subdir} extra = []string{"./" + subdir}
} }
return str.StringList("bzr", "export", "--format=zip", "-r", rev, "--root=prefix/", target, extra) return str.StringList("bzr", "export", "--format=zip", "-r", rev, "--root=prefix/", "--", target, extra)
}, },
}, },
"fossil": { "fossil": {
vcs: "fossil", vcs: "fossil",
init: func(remote string) []string { init: func(remote string) []string {
return []string{"fossil", "clone", remote, ".fossil"} return []string{"fossil", "clone", "--", remote, ".fossil"}
}, },
fetch: []string{"fossil", "pull", "-R", ".fossil"}, fetch: []string{"fossil", "pull", "-R", ".fossil"},
tags: func(remote string) []string { tags: func(remote string) []string {
@ -249,7 +249,7 @@ var vcsCmds = map[string]*vcsCmd{
} }
// Note that vcsRepo.ReadZip below rewrites this command // Note that vcsRepo.ReadZip below rewrites this command
// to run in a different directory, to work around a fossil bug. // to run in a different directory, to work around a fossil bug.
return str.StringList("fossil", "zip", "-R", ".fossil", "--name", "prefix", extra, rev, target) return str.StringList("fossil", "zip", "-R", ".fossil", "--name", "prefix", extra, "--", rev, target)
}, },
}, },
} }

View file

@ -208,6 +208,11 @@ func (r *codeRepo) Latest() (*RevInfo, error) {
return r.convert(info, "") return r.convert(info, "")
} }
// convert converts a version as reported by the code host to a version as
// interpreted by the module system.
//
// If statVers is a valid module version, it is used for the Version field.
// Otherwise, the Version is derived from the passed-in info and recent tags.
func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, error) { func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, error) {
info2 := &RevInfo{ info2 := &RevInfo{
Name: info.Name, Name: info.Name,
@ -268,7 +273,7 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
} }
// Otherwise make a pseudo-version. // Otherwise make a pseudo-version.
if info2.Version == "" { if info2.Version == "" {
tag, _ := r.code.RecentTag(statVers, p) tag, _ := r.code.RecentTag(info.Name, p)
v = tagToVersion(tag) v = tagToVersion(tag)
// TODO: Check that v is OK for r.pseudoMajor or else is OK for incompatible. // TODO: Check that v is OK for r.pseudoMajor or else is OK for incompatible.
info2.Version = PseudoVersion(r.pseudoMajor, v, info.Time, info.Short) info2.Version = PseudoVersion(r.pseudoMajor, v, info.Time, info.Short)

View file

@ -25,7 +25,7 @@ func TestMain(m *testing.M) {
} }
func testMain(m *testing.M) int { func testMain(m *testing.M) int {
SetProxy("direct") cfg.GOPROXY = "direct"
// The sum database is populated using a released version of the go command, // The sum database is populated using a released version of the go command,
// but this test may include fixes for additional modules that previously // but this test may include fixes for additional modules that previously
@ -48,11 +48,12 @@ const (
vgotest1hg = "vcs-test.golang.org/hg/vgotest1.hg" vgotest1hg = "vcs-test.golang.org/hg/vgotest1.hg"
) )
var altVgotests = []string{ var altVgotests = map[string]string{
vgotest1hg, "hg": vgotest1hg,
} }
type codeRepoTest struct { type codeRepoTest struct {
vcs string
path string path string
lookerr string lookerr string
mpath string mpath string
@ -70,6 +71,7 @@ type codeRepoTest struct {
var codeRepoTests = []codeRepoTest{ var codeRepoTests = []codeRepoTest{
{ {
vcs: "git",
path: "github.com/rsc/vgotest1", path: "github.com/rsc/vgotest1",
rev: "v0.0.0", rev: "v0.0.0",
version: "v0.0.0", version: "v0.0.0",
@ -83,6 +85,7 @@ var codeRepoTests = []codeRepoTest{
}, },
}, },
{ {
vcs: "git",
path: "github.com/rsc/vgotest1", path: "github.com/rsc/vgotest1",
rev: "v1.0.0", rev: "v1.0.0",
version: "v1.0.0", version: "v1.0.0",
@ -96,6 +99,7 @@ var codeRepoTests = []codeRepoTest{
}, },
}, },
{ {
vcs: "git",
path: "github.com/rsc/vgotest1/v2", path: "github.com/rsc/vgotest1/v2",
rev: "v2.0.0", rev: "v2.0.0",
version: "v2.0.0", version: "v2.0.0",
@ -105,6 +109,7 @@ var codeRepoTests = []codeRepoTest{
ziperr: "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0", ziperr: "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0",
}, },
{ {
vcs: "git",
path: "github.com/rsc/vgotest1", path: "github.com/rsc/vgotest1",
rev: "80d85c5", rev: "80d85c5",
version: "v1.0.0", version: "v1.0.0",
@ -118,6 +123,7 @@ var codeRepoTests = []codeRepoTest{
}, },
}, },
{ {
vcs: "git",
path: "github.com/rsc/vgotest1", path: "github.com/rsc/vgotest1",
rev: "mytag", rev: "mytag",
version: "v1.0.0", version: "v1.0.0",
@ -131,6 +137,7 @@ var codeRepoTests = []codeRepoTest{
}, },
}, },
{ {
vcs: "git",
path: "github.com/rsc/vgotest1/v2", path: "github.com/rsc/vgotest1/v2",
rev: "45f53230a", rev: "45f53230a",
version: "v2.0.0", version: "v2.0.0",
@ -141,6 +148,7 @@ var codeRepoTests = []codeRepoTest{
ziperr: "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0", ziperr: "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0",
}, },
{ {
vcs: "git",
path: "github.com/rsc/vgotest1/v54321", path: "github.com/rsc/vgotest1/v54321",
rev: "80d85c5", rev: "80d85c5",
version: "v54321.0.0-20180219231006-80d85c5d4d17", version: "v54321.0.0-20180219231006-80d85c5d4d17",
@ -150,16 +158,19 @@ var codeRepoTests = []codeRepoTest{
ziperr: "missing github.com/rsc/vgotest1/go.mod and .../v54321/go.mod at revision 80d85c5d4d17", ziperr: "missing github.com/rsc/vgotest1/go.mod and .../v54321/go.mod at revision 80d85c5d4d17",
}, },
{ {
vcs: "git",
path: "github.com/rsc/vgotest1/submod", path: "github.com/rsc/vgotest1/submod",
rev: "v1.0.0", rev: "v1.0.0",
err: "unknown revision submod/v1.0.0", err: "unknown revision submod/v1.0.0",
}, },
{ {
vcs: "git",
path: "github.com/rsc/vgotest1/submod", path: "github.com/rsc/vgotest1/submod",
rev: "v1.0.3", rev: "v1.0.3",
err: "unknown revision submod/v1.0.3", err: "unknown revision submod/v1.0.3",
}, },
{ {
vcs: "git",
path: "github.com/rsc/vgotest1/submod", path: "github.com/rsc/vgotest1/submod",
rev: "v1.0.4", rev: "v1.0.4",
version: "v1.0.4", version: "v1.0.4",
@ -174,6 +185,7 @@ var codeRepoTests = []codeRepoTest{
}, },
}, },
{ {
vcs: "git",
path: "github.com/rsc/vgotest1", path: "github.com/rsc/vgotest1",
rev: "v1.1.0", rev: "v1.1.0",
version: "v1.1.0", version: "v1.1.0",
@ -189,6 +201,7 @@ var codeRepoTests = []codeRepoTest{
}, },
}, },
{ {
vcs: "git",
path: "github.com/rsc/vgotest1/v2", path: "github.com/rsc/vgotest1/v2",
rev: "v2.0.1", rev: "v2.0.1",
version: "v2.0.1", version: "v2.0.1",
@ -198,6 +211,7 @@ var codeRepoTests = []codeRepoTest{
gomod: "module \"github.com/rsc/vgotest1/v2\" // root go.mod\n", gomod: "module \"github.com/rsc/vgotest1/v2\" // root go.mod\n",
}, },
{ {
vcs: "git",
path: "github.com/rsc/vgotest1/v2", path: "github.com/rsc/vgotest1/v2",
rev: "v2.0.3", rev: "v2.0.3",
version: "v2.0.3", version: "v2.0.3",
@ -207,6 +221,7 @@ var codeRepoTests = []codeRepoTest{
gomoderr: "github.com/rsc/vgotest1/v2/go.mod has non-.../v2 module path \"github.com/rsc/vgotest\" at revision v2.0.3", gomoderr: "github.com/rsc/vgotest1/v2/go.mod has non-.../v2 module path \"github.com/rsc/vgotest\" at revision v2.0.3",
}, },
{ {
vcs: "git",
path: "github.com/rsc/vgotest1/v2", path: "github.com/rsc/vgotest1/v2",
rev: "v2.0.4", rev: "v2.0.4",
version: "v2.0.4", version: "v2.0.4",
@ -216,6 +231,7 @@ var codeRepoTests = []codeRepoTest{
gomoderr: "github.com/rsc/vgotest1/go.mod and .../v2/go.mod both have .../v2 module paths at revision v2.0.4", gomoderr: "github.com/rsc/vgotest1/go.mod and .../v2/go.mod both have .../v2 module paths at revision v2.0.4",
}, },
{ {
vcs: "git",
path: "github.com/rsc/vgotest1/v2", path: "github.com/rsc/vgotest1/v2",
rev: "v2.0.5", rev: "v2.0.5",
version: "v2.0.5", version: "v2.0.5",
@ -226,6 +242,7 @@ var codeRepoTests = []codeRepoTest{
}, },
{ {
// redirect to github // redirect to github
vcs: "git",
path: "rsc.io/quote", path: "rsc.io/quote",
rev: "v1.0.0", rev: "v1.0.0",
version: "v1.0.0", version: "v1.0.0",
@ -236,6 +253,7 @@ var codeRepoTests = []codeRepoTest{
}, },
{ {
// redirect to static hosting proxy // redirect to static hosting proxy
vcs: "mod",
path: "swtch.com/testmod", path: "swtch.com/testmod",
rev: "v1.0.0", rev: "v1.0.0",
version: "v1.0.0", version: "v1.0.0",
@ -245,6 +263,7 @@ var codeRepoTests = []codeRepoTest{
}, },
{ {
// redirect to googlesource // redirect to googlesource
vcs: "git",
path: "golang.org/x/text", path: "golang.org/x/text",
rev: "4e4a3210bb", rev: "4e4a3210bb",
version: "v0.3.1-0.20180208041248-4e4a3210bb54", version: "v0.3.1-0.20180208041248-4e4a3210bb54",
@ -253,6 +272,7 @@ var codeRepoTests = []codeRepoTest{
time: time.Date(2018, 2, 8, 4, 12, 48, 0, time.UTC), time: time.Date(2018, 2, 8, 4, 12, 48, 0, time.UTC),
}, },
{ {
vcs: "git",
path: "github.com/pkg/errors", path: "github.com/pkg/errors",
rev: "v0.8.0", rev: "v0.8.0",
version: "v0.8.0", version: "v0.8.0",
@ -264,17 +284,20 @@ var codeRepoTests = []codeRepoTest{
// package in subdirectory - custom domain // package in subdirectory - custom domain
// In general we can't reject these definitively in Lookup, // In general we can't reject these definitively in Lookup,
// but gopkg.in is special. // but gopkg.in is special.
vcs: "git",
path: "gopkg.in/yaml.v2/abc", path: "gopkg.in/yaml.v2/abc",
lookerr: "invalid module path \"gopkg.in/yaml.v2/abc\"", lookerr: "invalid module path \"gopkg.in/yaml.v2/abc\"",
}, },
{ {
// package in subdirectory - github // package in subdirectory - github
// Because it's a package, Stat should fail entirely. // Because it's a package, Stat should fail entirely.
vcs: "git",
path: "github.com/rsc/quote/buggy", path: "github.com/rsc/quote/buggy",
rev: "c4d4236f", rev: "c4d4236f",
err: "missing github.com/rsc/quote/buggy/go.mod at revision c4d4236f9242", err: "missing github.com/rsc/quote/buggy/go.mod at revision c4d4236f9242",
}, },
{ {
vcs: "git",
path: "gopkg.in/yaml.v2", path: "gopkg.in/yaml.v2",
rev: "d670f940", rev: "d670f940",
version: "v2.0.0", version: "v2.0.0",
@ -284,6 +307,7 @@ var codeRepoTests = []codeRepoTest{
gomod: "module gopkg.in/yaml.v2\n", gomod: "module gopkg.in/yaml.v2\n",
}, },
{ {
vcs: "git",
path: "gopkg.in/check.v1", path: "gopkg.in/check.v1",
rev: "20d25e280405", rev: "20d25e280405",
version: "v1.0.0-20161208181325-20d25e280405", version: "v1.0.0-20161208181325-20d25e280405",
@ -293,6 +317,7 @@ var codeRepoTests = []codeRepoTest{
gomod: "module gopkg.in/check.v1\n", gomod: "module gopkg.in/check.v1\n",
}, },
{ {
vcs: "git",
path: "gopkg.in/yaml.v2", path: "gopkg.in/yaml.v2",
rev: "v2", rev: "v2",
version: "v2.2.3-0.20190319135612-7b8349ac747c", version: "v2.2.3-0.20190319135612-7b8349ac747c",
@ -302,6 +327,7 @@ var codeRepoTests = []codeRepoTest{
gomod: "module \"gopkg.in/yaml.v2\"\n\nrequire (\n\t\"gopkg.in/check.v1\" v0.0.0-20161208181325-20d25e280405\n)\n", gomod: "module \"gopkg.in/yaml.v2\"\n\nrequire (\n\t\"gopkg.in/check.v1\" v0.0.0-20161208181325-20d25e280405\n)\n",
}, },
{ {
vcs: "git",
path: "vcs-test.golang.org/go/mod/gitrepo1", path: "vcs-test.golang.org/go/mod/gitrepo1",
rev: "master", rev: "master",
version: "v1.2.4-annotated", version: "v1.2.4-annotated",
@ -311,6 +337,7 @@ var codeRepoTests = []codeRepoTest{
gomod: "module vcs-test.golang.org/go/mod/gitrepo1\n", gomod: "module vcs-test.golang.org/go/mod/gitrepo1\n",
}, },
{ {
vcs: "git",
path: "gopkg.in/natefinch/lumberjack.v2", path: "gopkg.in/natefinch/lumberjack.v2",
rev: "latest", rev: "latest",
version: "v2.0.0-20170531160350-a96e63847dc3", version: "v2.0.0-20170531160350-a96e63847dc3",
@ -320,6 +347,7 @@ var codeRepoTests = []codeRepoTest{
gomod: "module gopkg.in/natefinch/lumberjack.v2\n", gomod: "module gopkg.in/natefinch/lumberjack.v2\n",
}, },
{ {
vcs: "git",
path: "gopkg.in/natefinch/lumberjack.v2", path: "gopkg.in/natefinch/lumberjack.v2",
// This repo has a v2.1 tag. // This repo has a v2.1 tag.
// We only allow semver references to tags that are fully qualified, as in v2.1.0. // We only allow semver references to tags that are fully qualified, as in v2.1.0.
@ -335,6 +363,7 @@ var codeRepoTests = []codeRepoTest{
gomod: "module gopkg.in/natefinch/lumberjack.v2\n", gomod: "module gopkg.in/natefinch/lumberjack.v2\n",
}, },
{ {
vcs: "git",
path: "vcs-test.golang.org/go/v2module/v2", path: "vcs-test.golang.org/go/v2module/v2",
rev: "v2.0.0", rev: "v2.0.0",
version: "v2.0.0", version: "v2.0.0",
@ -359,8 +388,11 @@ func TestCodeRepo(t *testing.T) {
f := func(tt codeRepoTest) func(t *testing.T) { f := func(tt codeRepoTest) func(t *testing.T) {
return func(t *testing.T) { return func(t *testing.T) {
t.Parallel() t.Parallel()
if tt.vcs != "mod" {
testenv.MustHaveExecPath(t, tt.vcs)
}
repo, err := Lookup(tt.path) repo, err := Lookup("direct", tt.path)
if tt.lookerr != "" { if tt.lookerr != "" {
if err != nil && err.Error() == tt.lookerr { if err != nil && err.Error() == tt.lookerr {
return return
@ -457,9 +489,10 @@ func TestCodeRepo(t *testing.T) {
} }
t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.rev, f(tt)) t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.rev, f(tt))
if strings.HasPrefix(tt.path, vgotest1git) { if strings.HasPrefix(tt.path, vgotest1git) {
for _, alt := range altVgotests { for vcs, alt := range altVgotests {
// Note: Communicating with f through tt; should be cleaned up. // Note: Communicating with f through tt; should be cleaned up.
old := tt old := tt
tt.vcs = vcs
tt.path = alt + strings.TrimPrefix(tt.path, vgotest1git) tt.path = alt + strings.TrimPrefix(tt.path, vgotest1git)
if strings.HasPrefix(tt.mpath, vgotest1git) { if strings.HasPrefix(tt.mpath, vgotest1git) {
tt.mpath = alt + strings.TrimPrefix(tt.mpath, vgotest1git) tt.mpath = alt + strings.TrimPrefix(tt.mpath, vgotest1git)
@ -515,32 +548,39 @@ func remap(name string, m map[string]string) string {
} }
var codeRepoVersionsTests = []struct { var codeRepoVersionsTests = []struct {
vcs string
path string path string
prefix string prefix string
versions []string versions []string
}{ }{
{ {
vcs: "git",
path: "github.com/rsc/vgotest1", path: "github.com/rsc/vgotest1",
versions: []string{"v0.0.0", "v0.0.1", "v1.0.0", "v1.0.1", "v1.0.2", "v1.0.3", "v1.1.0", "v2.0.0+incompatible"}, versions: []string{"v0.0.0", "v0.0.1", "v1.0.0", "v1.0.1", "v1.0.2", "v1.0.3", "v1.1.0", "v2.0.0+incompatible"},
}, },
{ {
vcs: "git",
path: "github.com/rsc/vgotest1", path: "github.com/rsc/vgotest1",
prefix: "v1.0", prefix: "v1.0",
versions: []string{"v1.0.0", "v1.0.1", "v1.0.2", "v1.0.3"}, versions: []string{"v1.0.0", "v1.0.1", "v1.0.2", "v1.0.3"},
}, },
{ {
vcs: "git",
path: "github.com/rsc/vgotest1/v2", path: "github.com/rsc/vgotest1/v2",
versions: []string{"v2.0.0", "v2.0.1", "v2.0.2", "v2.0.3", "v2.0.4", "v2.0.5", "v2.0.6"}, versions: []string{"v2.0.0", "v2.0.1", "v2.0.2", "v2.0.3", "v2.0.4", "v2.0.5", "v2.0.6"},
}, },
{ {
vcs: "mod",
path: "swtch.com/testmod", path: "swtch.com/testmod",
versions: []string{"v1.0.0", "v1.1.1"}, versions: []string{"v1.0.0", "v1.1.1"},
}, },
{ {
vcs: "git",
path: "gopkg.in/russross/blackfriday.v2", path: "gopkg.in/russross/blackfriday.v2",
versions: []string{"v2.0.0", "v2.0.1"}, versions: []string{"v2.0.0", "v2.0.1"},
}, },
{ {
vcs: "git",
path: "gopkg.in/natefinch/lumberjack.v2", path: "gopkg.in/natefinch/lumberjack.v2",
versions: []string{"v2.0.0"}, versions: []string{"v2.0.0"},
}, },
@ -560,8 +600,11 @@ func TestCodeRepoVersions(t *testing.T) {
t.Run(strings.ReplaceAll(tt.path, "/", "_"), func(t *testing.T) { t.Run(strings.ReplaceAll(tt.path, "/", "_"), func(t *testing.T) {
tt := tt tt := tt
t.Parallel() t.Parallel()
if tt.vcs != "mod" {
testenv.MustHaveExecPath(t, tt.vcs)
}
repo, err := Lookup(tt.path) repo, err := Lookup("direct", tt.path)
if err != nil { if err != nil {
t.Fatalf("Lookup(%q): %v", tt.path, err) t.Fatalf("Lookup(%q): %v", tt.path, err)
} }
@ -578,23 +621,28 @@ func TestCodeRepoVersions(t *testing.T) {
} }
var latestTests = []struct { var latestTests = []struct {
vcs string
path string path string
version string version string
err string err string
}{ }{
{ {
vcs: "git",
path: "github.com/rsc/empty", path: "github.com/rsc/empty",
err: "no commits", err: "no commits",
}, },
{ {
vcs: "git",
path: "github.com/rsc/vgotest1", path: "github.com/rsc/vgotest1",
version: "v0.0.0-20180219223237-a08abb797a67", version: "v0.0.0-20180219223237-a08abb797a67",
}, },
{ {
vcs: "git",
path: "github.com/rsc/vgotest1/subdir", path: "github.com/rsc/vgotest1/subdir",
err: "missing github.com/rsc/vgotest1/subdir/go.mod at revision a08abb797a67", err: "missing github.com/rsc/vgotest1/subdir/go.mod at revision a08abb797a67",
}, },
{ {
vcs: "mod",
path: "swtch.com/testmod", path: "swtch.com/testmod",
version: "v1.1.1", version: "v1.1.1",
}, },
@ -615,8 +663,11 @@ func TestLatest(t *testing.T) {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
tt := tt tt := tt
t.Parallel() t.Parallel()
if tt.vcs != "mod" {
testenv.MustHaveExecPath(t, tt.vcs)
}
repo, err := Lookup(tt.path) repo, err := Lookup("direct", tt.path)
if err != nil { if err != nil {
t.Fatalf("Lookup(%q): %v", tt.path, err) t.Fatalf("Lookup(%q): %v", tt.path, err)
} }

View file

@ -205,13 +205,16 @@ func downloadZip(mod module.Version, zipfile string) (err error) {
} }
}() }()
repo, err := Lookup(mod.Path) err = TryProxies(func(proxy string) error {
repo, err := Lookup(proxy, mod.Path)
if err != nil {
return err
}
return repo.Zip(f, mod.Version)
})
if err != nil { if err != nil {
return err return err
} }
if err := repo.Zip(f, mod.Version); err != nil {
return err
}
// Double-check that the paths within the zip file are well-formed. // Double-check that the paths within the zip file are well-formed.
// //
@ -290,7 +293,7 @@ func initGoSum() bool {
goSum.m = make(map[module.Version][]string) goSum.m = make(map[module.Version][]string)
goSum.checked = make(map[modSum]bool) goSum.checked = make(map[modSum]bool)
data, err := ioutil.ReadFile(GoSumFile) data, err := renameio.ReadFile(GoSumFile)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
base.Fatalf("go: %v", err) base.Fatalf("go: %v", err)
} }
@ -300,7 +303,7 @@ func initGoSum() bool {
// Add old go.modverify file. // Add old go.modverify file.
// We'll delete go.modverify in WriteGoSum. // We'll delete go.modverify in WriteGoSum.
alt := strings.TrimSuffix(GoSumFile, ".sum") + ".modverify" alt := strings.TrimSuffix(GoSumFile, ".sum") + ".modverify"
if data, err := ioutil.ReadFile(alt); err == nil { if data, err := renameio.ReadFile(alt); err == nil {
migrate := make(map[module.Version][]string) migrate := make(map[module.Version][]string)
readGoSum(migrate, alt, data) readGoSum(migrate, alt, data)
for mod, sums := range migrate { for mod, sums := range migrate {
@ -360,7 +363,7 @@ func checkMod(mod module.Version) {
if err != nil { if err != nil {
base.Fatalf("verifying %s@%s: %v", mod.Path, mod.Version, err) base.Fatalf("verifying %s@%s: %v", mod.Path, mod.Version, err)
} }
data, err := ioutil.ReadFile(ziphash) data, err := renameio.ReadFile(ziphash)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
// This can happen if someone does rm -rf GOPATH/src/cache/download. So it goes. // This can happen if someone does rm -rf GOPATH/src/cache/download. So it goes.
@ -487,7 +490,7 @@ func Sum(mod module.Version) string {
if err != nil { if err != nil {
return "" return ""
} }
data, err := ioutil.ReadFile(ziphash) data, err := renameio.ReadFile(ziphash)
if err != nil { if err != nil {
return "" return ""
} }
@ -535,7 +538,7 @@ func WriteGoSum() {
if !goSum.overwrite { if !goSum.overwrite {
// Re-read the go.sum file to incorporate any sums added by other processes // Re-read the go.sum file to incorporate any sums added by other processes
// in the meantime. // in the meantime.
data, err := ioutil.ReadFile(GoSumFile) data, err := renameio.ReadFile(GoSumFile)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
base.Fatalf("go: re-reading go.sum: %v", err) base.Fatalf("go: re-reading go.sum: %v", err)
} }
@ -628,7 +631,7 @@ For more information, see 'go help module-auth'.
` `
var HelpSum = &base.Command{ var HelpModuleAuth = &base.Command{
UsageLine: "module-auth", UsageLine: "module-auth",
Short: "module authentication using go.sum", Short: "module authentication using go.sum",
Long: ` Long: `
@ -709,18 +712,56 @@ If GOSUMDB is set to "off", or if "go get" is invoked with the -insecure flag,
the checksum database is not consulted, and all unrecognized modules are the checksum database is not consulted, and all unrecognized modules are
accepted, at the cost of giving up the security guarantee of verified repeatable accepted, at the cost of giving up the security guarantee of verified repeatable
downloads for all modules. A better way to bypass the checksum database downloads for all modules. A better way to bypass the checksum database
for specific modules is to use the GONOSUMDB environment variable. for specific modules is to use the GOPRIVATE or GONOSUMDB environment
variables. See 'go help module-private' for details.
The GONOSUMDB environment variable is a comma-separated list of
glob patterns (in the syntax of Go's path.Match) of module path prefixes The 'go env -w' command (see 'go help env') can be used to set these variables
that should not be compared against the checksum database. for future go command invocations.
For example, `,
}
GONOSUMDB=*.corp.example.com,rsc.io/private
var HelpModulePrivate = &base.Command{
disables checksum database lookups for modules with path prefixes matching UsageLine: "module-private",
either pattern, including "git.corp.example.com/xyzzy", "rsc.io/private", Short: "module configuration for non-public modules",
and "rsc.io/private/quux". Long: `
The go command defaults to downloading modules from the public Go module
mirror at proxy.golang.org. It also defaults to validating downloaded modules,
regardless of source, against the public Go checksum database at sum.golang.org.
These defaults work well for publicly available source code.
The GOPRIVATE environment variable controls which modules the go command
considers to be private (not available publicly) and should therefore not use the
proxy or checksum database. The variable is a comma-separated list of
glob patterns (in the syntax of Go's path.Match) of module path prefixes.
For example,
GOPRIVATE=*.corp.example.com,rsc.io/private
causes the go command to treat as private any module with a path prefix
matching either pattern, including git.corp.example.com/xyzzy, rsc.io/private,
and rsc.io/private/quux.
The GOPRIVATE environment variable may be used by other tools as well to
identify non-public modules. For example, an editor could use GOPRIVATE
to decide whether to hyperlink a package import to a godoc.org page.
For fine-grained control over module download and validation, the GONOPROXY
and GONOSUMDB environment variables accept the same kind of glob list
and override GOPRIVATE for the specific decision of whether to use the proxy
and checksum database, respectively.
For example, if a company ran a module proxy serving private modules,
users would configure go using:
GOPRIVATE=*.corp.example.com
GOPROXY=proxy.example.com
GONOPROXY=none
This would tell the go comamnd and other tools that modules beginning with
a corp.example.com subdomain are private but that the company proxy should
be used for downloading both public and private modules, because
GONOPROXY has been set to a pattern that won't match any modules,
overriding GOPRIVATE.
The 'go env -w' command (see 'go help env') can be used to set these variables The 'go env -w' command (see 'go help env') can be used to set these variables
for future go command invocations. for future go command invocations.

View file

@ -12,13 +12,12 @@ import (
"io/ioutil" "io/ioutil"
"net/url" "net/url"
"os" "os"
"path"
pathpkg "path" pathpkg "path"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
"sync" "sync"
"time" "time"
"unicode"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg" "cmd/go/internal/cfg"
@ -84,16 +83,6 @@ cached module versions with GOPROXY=https://example.com/proxy.
`, `,
} }
var proxyURL = cfg.Getenv("GOPROXY")
// SetProxy sets the proxy to use when fetching modules.
// It accepts the same syntax as the GOPROXY environment variable,
// which also provides its default configuration.
// SetProxy must not be called after the first module fetch has begun.
func SetProxy(url string) {
proxyURL = url
}
var proxyOnce struct { var proxyOnce struct {
sync.Once sync.Once
list []string list []string
@ -102,13 +91,32 @@ var proxyOnce struct {
func proxyURLs() ([]string, error) { func proxyURLs() ([]string, error) {
proxyOnce.Do(func() { proxyOnce.Do(func() {
for _, proxyURL := range strings.Split(proxyURL, ",") { if cfg.GONOPROXY != "" && cfg.GOPROXY != "direct" {
proxyOnce.list = append(proxyOnce.list, "noproxy")
}
for _, proxyURL := range strings.Split(cfg.GOPROXY, ",") {
proxyURL = strings.TrimSpace(proxyURL)
if proxyURL == "" { if proxyURL == "" {
continue continue
} }
if proxyURL == "off" {
// "off" always fails hard, so can stop walking list.
proxyOnce.list = append(proxyOnce.list, "off")
break
}
if proxyURL == "direct" { if proxyURL == "direct" {
proxyOnce.list = append(proxyOnce.list, "direct") proxyOnce.list = append(proxyOnce.list, "direct")
continue // For now, "direct" is the end of the line. We may decide to add some
// sort of fallback behavior for them in the future, so ignore
// subsequent entries for forward-compatibility.
break
}
// Single-word tokens are reserved for built-in behaviors, and anything
// containing the string ":/" or matching an absolute file path must be a
// complete URL. For all other paths, implicitly add "https://".
if strings.ContainsAny(proxyURL, ".:/") && !strings.Contains(proxyURL, ":/") && !filepath.IsAbs(proxyURL) && !path.IsAbs(proxyURL) {
proxyURL = "https://" + proxyURL
} }
// Check that newProxyRepo accepts the URL. // Check that newProxyRepo accepts the URL.
@ -125,32 +133,30 @@ func proxyURLs() ([]string, error) {
return proxyOnce.list, proxyOnce.err return proxyOnce.list, proxyOnce.err
} }
func lookupProxy(path string) (Repo, error) { // TryProxies iterates f over each configured proxy (including "noproxy" and
list, err := proxyURLs() // "direct" if applicable) until f returns an error that is not
// equivalent to os.ErrNotExist.
//
// TryProxies then returns that final error.
//
// If GOPROXY is set to "off", TryProxies invokes f once with the argument
// "off".
func TryProxies(f func(proxy string) error) error {
proxies, err := proxyURLs()
if err != nil { if err != nil {
return nil, err return err
}
if len(proxies) == 0 {
return f("off")
} }
var repos listRepo for _, proxy := range proxies {
for _, u := range list { err = f(proxy)
var r Repo if !errors.Is(err, os.ErrNotExist) {
if u == "direct" { break
// lookupDirect does actual network traffic.
// Especially if GOPROXY="http://mainproxy,direct",
// avoid the network until we need it by using a lazyRepo wrapper.
r = &lazyRepo{setup: lookupDirect, path: path}
} else {
// The URL itself was checked in proxyURLs.
// The only possible error here is a bad path,
// so we can return it unconditionally.
r, err = newProxyRepo(u, path)
if err != nil {
return nil, err
}
} }
repos = append(repos, r)
} }
return repos, nil return err
} }
type proxyRepo struct { type proxyRepo struct {
@ -201,18 +207,6 @@ func (p *proxyRepo) getBytes(path string) ([]byte, error) {
func (p *proxyRepo) getBody(path string) (io.ReadCloser, error) { func (p *proxyRepo) getBody(path string) (io.ReadCloser, error) {
fullPath := pathpkg.Join(p.url.Path, path) fullPath := pathpkg.Join(p.url.Path, path)
if p.url.Scheme == "file" {
rawPath, err := url.PathUnescape(fullPath)
if err != nil {
return nil, err
}
if runtime.GOOS == "windows" && len(rawPath) >= 4 && rawPath[0] == '/' && unicode.IsLetter(rune(rawPath[1])) && rawPath[2] == ':' {
// On Windows, file URLs look like "file:///C:/foo/bar". url.Path will
// start with a slash which must be removed. See golang.org/issue/6027.
rawPath = rawPath[1:]
}
return os.Open(filepath.FromSlash(rawPath))
}
target := *p.url target := *p.url
target.Path = fullPath target.Path = fullPath
@ -342,117 +336,3 @@ func (p *proxyRepo) Zip(dst io.Writer, version string) error {
func pathEscape(s string) string { func pathEscape(s string) string {
return strings.ReplaceAll(url.PathEscape(s), "%2F", "/") return strings.ReplaceAll(url.PathEscape(s), "%2F", "/")
} }
// A lazyRepo is a lazily-initialized Repo,
// constructed on demand by calling setup.
type lazyRepo struct {
path string
setup func(string) (Repo, error)
once sync.Once
repo Repo
err error
}
func (r *lazyRepo) init() {
r.repo, r.err = r.setup(r.path)
}
func (r *lazyRepo) ModulePath() string {
return r.path
}
func (r *lazyRepo) Versions(prefix string) ([]string, error) {
if r.once.Do(r.init); r.err != nil {
return nil, r.err
}
return r.repo.Versions(prefix)
}
func (r *lazyRepo) Stat(rev string) (*RevInfo, error) {
if r.once.Do(r.init); r.err != nil {
return nil, r.err
}
return r.repo.Stat(rev)
}
func (r *lazyRepo) Latest() (*RevInfo, error) {
if r.once.Do(r.init); r.err != nil {
return nil, r.err
}
return r.repo.Latest()
}
func (r *lazyRepo) GoMod(version string) ([]byte, error) {
if r.once.Do(r.init); r.err != nil {
return nil, r.err
}
return r.repo.GoMod(version)
}
func (r *lazyRepo) Zip(dst io.Writer, version string) error {
if r.once.Do(r.init); r.err != nil {
return r.err
}
return r.repo.Zip(dst, version)
}
// A listRepo is a preference list of Repos.
// The list must be non-empty and all Repos
// must return the same result from ModulePath.
// For each method, the repos are tried in order
// until one succeeds or returns a non-ErrNotExist (non-404) error.
type listRepo []Repo
func (l listRepo) ModulePath() string {
return l[0].ModulePath()
}
func (l listRepo) Versions(prefix string) ([]string, error) {
for i, r := range l {
v, err := r.Versions(prefix)
if i == len(l)-1 || !errors.Is(err, os.ErrNotExist) {
return v, err
}
}
panic("no repos")
}
func (l listRepo) Stat(rev string) (*RevInfo, error) {
for i, r := range l {
info, err := r.Stat(rev)
if i == len(l)-1 || !errors.Is(err, os.ErrNotExist) {
return info, err
}
}
panic("no repos")
}
func (l listRepo) Latest() (*RevInfo, error) {
for i, r := range l {
info, err := r.Latest()
if i == len(l)-1 || !errors.Is(err, os.ErrNotExist) {
return info, err
}
}
panic("no repos")
}
func (l listRepo) GoMod(version string) ([]byte, error) {
for i, r := range l {
data, err := r.GoMod(version)
if i == len(l)-1 || !errors.Is(err, os.ErrNotExist) {
return data, err
}
}
panic("no repos")
}
func (l listRepo) Zip(dst io.Writer, version string) error {
for i, r := range l {
err := r.Zip(dst, version)
if i == len(l)-1 || !errors.Is(err, os.ErrNotExist) {
return err
}
}
panic("no repos")
}

View file

@ -5,6 +5,7 @@
package modfetch package modfetch
import ( import (
"errors"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -172,20 +173,32 @@ type RevInfo struct {
var lookupCache par.Cache var lookupCache par.Cache
// Lookup returns the module with the given module path. type lookupCacheKey struct {
proxy, path string
}
// Lookup returns the module with the given module path,
// fetched through the given proxy.
//
// The distinguished proxy "direct" indicates that the path should be fetched
// from its origin, and "noproxy" indicates that the patch should be fetched
// directly only if GONOPROXY matches the given path.
//
// For the distinguished proxy "off", Lookup always returns a non-nil error.
//
// A successful return does not guarantee that the module // A successful return does not guarantee that the module
// has any defined versions. // has any defined versions.
func Lookup(path string) (Repo, error) { func Lookup(proxy, path string) (Repo, error) {
if traceRepo { if traceRepo {
defer logCall("Lookup(%q)", path)() defer logCall("Lookup(%q, %q)", proxy, path)()
} }
type cached struct { type cached struct {
r Repo r Repo
err error err error
} }
c := lookupCache.Do(path, func() interface{} { c := lookupCache.Do(lookupCacheKey{proxy, path}, func() interface{} {
r, err := lookup(path) r, err := lookup(proxy, path)
if err == nil { if err == nil {
if traceRepo { if traceRepo {
r = newLoggingRepo(r) r = newLoggingRepo(r)
@ -199,19 +212,39 @@ func Lookup(path string) (Repo, error) {
} }
// lookup returns the module with the given module path. // lookup returns the module with the given module path.
func lookup(path string) (r Repo, err error) { func lookup(proxy, path string) (r Repo, err error) {
if cfg.BuildMod == "vendor" { if cfg.BuildMod == "vendor" {
return nil, fmt.Errorf("module lookup disabled by -mod=%s", cfg.BuildMod) return nil, errModVendor
} }
if proxyURL == "off" {
return nil, fmt.Errorf("module lookup disabled by GOPROXY=%s", proxyURL) if str.GlobsMatchPath(cfg.GONOPROXY, path) {
switch proxy {
case "noproxy", "direct":
return lookupDirect(path)
default:
return nil, errNoproxy
}
} }
if proxyURL != "" && proxyURL != "direct" && !str.GlobsMatchPath(cfg.GONOPROXY, path) {
return lookupProxy(path) switch proxy {
case "off":
return nil, errProxyOff
case "direct":
return lookupDirect(path)
case "noproxy":
return nil, errUseProxy
default:
return newProxyRepo(proxy, path)
} }
return lookupDirect(path)
} }
var (
errModVendor = errors.New("module lookup disabled by -mod=vendor")
errProxyOff = errors.New("module lookup disabled by GOPROXY=off")
errNoproxy error = notExistError("disabled by GOPRIVATE/GONOPROXY")
errUseProxy error = notExistError("path does not match GOPRIVATE/GONOPROXY")
)
func lookupDirect(path string) (Repo, error) { func lookupDirect(path string) (Repo, error) {
security := web.SecureOnly security := web.SecureOnly
if get.Insecure { if get.Insecure {
@ -220,7 +253,7 @@ func lookupDirect(path string) (Repo, error) {
rr, err := get.RepoRootForImportPath(path, get.PreferMod, security) rr, err := get.RepoRootForImportPath(path, get.PreferMod, security)
if err != nil { if err != nil {
// We don't know where to find code for a module with this path. // We don't know where to find code for a module with this path.
return nil, err return nil, notExistError(err.Error())
} }
if rr.VCS == "mod" { if rr.VCS == "mod" {
@ -286,7 +319,7 @@ func ImportRepoRev(path, rev string) (Repo, *RevInfo, error) {
return nil, nil, err return nil, nil, err
} }
info, err := repo.(*codeRepo).convert(revInfo, "") info, err := repo.(*codeRepo).convert(revInfo, rev)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -362,3 +395,13 @@ func (l *loggingRepo) Zip(dst io.Writer, version string) error {
defer logCall("Repo[%s]: Zip(%s, %q)", l.r.ModulePath(), dstName, version)() defer logCall("Repo[%s]: Zip(%s, %q)", l.r.ModulePath(), dstName, version)()
return l.r.Zip(dst, version) return l.r.Zip(dst, version)
} }
// A notExistError is like os.ErrNotExist, but with a custom message
type notExistError string
func (e notExistError) Error() string {
return string(e)
}
func (notExistError) Is(target error) bool {
return target == os.ErrNotExist
}

View file

@ -142,7 +142,10 @@ func (c *dbClient) initBase() {
return return
} }
for _, proxyURL := range urls { for _, proxyURL := range urls {
if proxyURL == "direct" { if proxyURL == "noproxy" {
continue
}
if proxyURL == "direct" || proxyURL == "off" {
break break
} }
proxy, err := url.Parse(proxyURL) proxy, err := url.Parse(proxyURL)

View file

@ -9,15 +9,14 @@ import (
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg" "cmd/go/internal/cfg"
"cmd/go/internal/get" "cmd/go/internal/get"
"cmd/go/internal/imports"
"cmd/go/internal/load" "cmd/go/internal/load"
"cmd/go/internal/modfetch"
"cmd/go/internal/modload" "cmd/go/internal/modload"
"cmd/go/internal/module" "cmd/go/internal/module"
"cmd/go/internal/mvs" "cmd/go/internal/mvs"
"cmd/go/internal/par" "cmd/go/internal/par"
"cmd/go/internal/search" "cmd/go/internal/search"
"cmd/go/internal/semver" "cmd/go/internal/semver"
"cmd/go/internal/str"
"cmd/go/internal/work" "cmd/go/internal/work"
"errors" "errors"
"fmt" "fmt"
@ -29,9 +28,9 @@ import (
) )
var CmdGet = &base.Command{ var CmdGet = &base.Command{
// Note: -d -m -u are listed explicitly because they are the most common get flags. // Note: -d -u are listed explicitly because they are the most common get flags.
// Do not send CLs removing them because they're covered by [get flags]. // Do not send CLs removing them because they're covered by [get flags].
UsageLine: "go get [-d] [-m] [-t] [-u] [-v] [-insecure] [build flags] [packages]", UsageLine: "go get [-d] [-t] [-u] [-v] [-insecure] [build flags] [packages]",
Short: "add dependencies to current module and install them", Short: "add dependencies to current module and install them",
Long: ` Long: `
Get resolves and adds dependencies to the current development module Get resolves and adds dependencies to the current development module
@ -60,11 +59,12 @@ dependency should be removed entirely, downgrading or removing modules
depending on it as needed. depending on it as needed.
The version suffix @latest explicitly requests the latest minor release of the The version suffix @latest explicitly requests the latest minor release of the
given path. given path. The suffix @patch requests the latest patch release: if the path
is already in the build list, the selected version will have the same minor
The suffix @patch requests the latest patch release: if the path is already in version. If the path is not already in the build list, @patch is equivalent
the build list, the selected version will have the same minor version. to @latest. Neither @latest nor @patch will cause 'go get' to downgrade a module
If the path is not already in the build list, @patch is equivalent to @latest. in the build list if it is required at a newer pre-release version that is
newer than the latest released version.
Although get defaults to using the latest version of the module containing Although get defaults to using the latest version of the module containing
a named package, it does not use the latest version of that module's a named package, it does not use the latest version of that module's
@ -78,9 +78,12 @@ those requirements by taking the maximum requested version.)
The -t flag instructs get to consider modules needed to build tests of The -t flag instructs get to consider modules needed to build tests of
packages specified on the command line. packages specified on the command line.
The -u flag instructs get to update dependencies to use newer minor or The -u flag instructs get to update modules providing dependencies
patch releases when available. Continuing the previous example, of packages named on the command line to use newer minor or patch
'go get -u A' will use the latest A with B v1.3.1 (not B v1.2.3). releases when available. Continuing the previous example, 'go get -u A'
will use the latest A with B v1.3.1 (not B v1.2.3). If B requires module C,
but C does not provide any packages needed to build packages in A
(not including tests), then C will not be updated.
The -u=patch flag (not -u patch) also instructs get to update dependencies, The -u=patch flag (not -u patch) also instructs get to update dependencies,
but changes the default to select patch releases. but changes the default to select patch releases.
@ -97,18 +100,6 @@ this automatically. Similarly, downgrading one dependency may
require downgrading other dependencies, and 'go get' does require downgrading other dependencies, and 'go get' does
this automatically as well. this automatically as well.
The -m flag instructs get to stop here, after resolving, upgrading,
and downgrading modules and updating go.mod. When using -m,
each specified package path must be a module path as well,
not the import path of a package below the module root.
When the -m and -u flags are used together, 'go get' will upgrade
modules that provide packages depended on by the modules named on
the command line. For example, 'go get -u -m A' will upgrade A and
any module providing packages imported by packages in A.
'go get -u -m' will upgrade modules that provided packages needed
by the main module.
The -insecure flag permits fetching from repositories and resolving The -insecure flag permits fetching from repositories and resolving
custom domains using insecure schemes such as HTTP. Use with caution. custom domains using insecure schemes such as HTTP. Use with caution.
@ -227,12 +218,14 @@ type querySpec struct {
// vers specifies what version of the module to get. // vers specifies what version of the module to get.
vers string vers string
// forceModulePath is true if path should be interpreted as a module path // forceModulePath is true if path should be interpreted as a module path.
// even if -m is not specified. // If forceModulePath is true, prevM must be set.
forceModulePath bool forceModulePath bool
// prevM is the previous version of the module. prevM is needed // prevM is the previous version of the module. prevM is needed
// if vers is "patch", and the module was previously in the build list. // to determine the minor version number if vers is "patch". It's also
// used to avoid downgrades from prerelease versions newer than
// "latest" and "patch". If prevM is set, forceModulePath must be true.
prevM module.Version prevM module.Version
} }
@ -267,13 +260,21 @@ func runGet(cmd *base.Command, args []string) {
if *getFix { if *getFix {
fmt.Fprintf(os.Stderr, "go get: -fix flag is a no-op when using modules\n") fmt.Fprintf(os.Stderr, "go get: -fix flag is a no-op when using modules\n")
} }
if *getM {
base.Fatalf("go get: -m flag is no longer supported; consider -d to skip building packages")
}
modload.LoadTests = *getT modload.LoadTests = *getT
if cfg.BuildMod == "vendor" { if cfg.BuildMod == "vendor" {
base.Fatalf("go get: disabled by -mod=%s", cfg.BuildMod) base.Fatalf("go get: disabled by -mod=%s", cfg.BuildMod)
} }
modload.LoadBuildList() buildList := modload.LoadBuildList()
buildList = buildList[:len(buildList):len(buildList)] // copy on append
versionByPath := make(map[string]string)
for _, m := range buildList {
versionByPath[m.Path] = m.Version
}
// Do not allow any updating of go.mod until we've applied // Do not allow any updating of go.mod until we've applied
// all the requested changes and checked that the result matches // all the requested changes and checked that the result matches
@ -318,12 +319,7 @@ func runGet(cmd *base.Command, args []string) {
// contains no wildcards (...), check that it is a package in // contains no wildcards (...), check that it is a package in
// the main module. If the path contains wildcards but matches no // the main module. If the path contains wildcards but matches no
// packages, we'll warn after package loading. // packages, we'll warn after package loading.
if len(args) > 0 && *getM { if !strings.Contains(path, "...") {
base.Errorf("go get %s: -m requires a module path, but a relative path must be a package in the main module", arg)
continue
}
if !*getM && !strings.Contains(path, "...") {
pkgPath := modload.DirImportPath(filepath.FromSlash(path)) pkgPath := modload.DirImportPath(filepath.FromSlash(path))
if pkgs := modload.TargetPackages(pkgPath); len(pkgs) == 0 { if pkgs := modload.TargetPackages(pkgPath); len(pkgs) == 0 {
abs, err := filepath.Abs(path) abs, err := filepath.Abs(path)
@ -341,25 +337,7 @@ func runGet(cmd *base.Command, args []string) {
} }
case strings.Contains(path, "..."): case strings.Contains(path, "..."):
// If we're using -m, look up modules in the build list that match // Wait until we load packages to look up modules.
// the pattern. Report an error if no modules match.
if *getM {
match := search.MatchPattern(path)
matched := false
for _, m := range modload.BuildList() {
if match(m.Path) || str.HasPathPrefix(path, m.Path) {
queries = append(queries, &query{querySpec: querySpec{path: m.Path, vers: vers, prevM: m, forceModulePath: true}, arg: arg})
matched = true
}
}
if !matched {
base.Errorf("go get %s: pattern matches no modules in build list", arg)
continue
}
break
}
// If we're not using -m, wait until we load packages to look up modules.
// We don't know yet whether any modules in the build list provide // We don't know yet whether any modules in the build list provide
// packages matching the pattern. For example, suppose // packages matching the pattern. For example, suppose
// golang.org/x/tools and golang.org/x/tools/playground are separate // golang.org/x/tools and golang.org/x/tools/playground are separate
@ -369,50 +347,83 @@ func runGet(cmd *base.Command, args []string) {
// upgrade golang.org/x/tools. // upgrade golang.org/x/tools.
case path == "all": case path == "all":
// This is the package pattern "all" not the module pattern "all", // Don't query modules until we load packages. We'll automatically
// even if *getM. We won't create any queries yet, since we're going to // look up any missing modules.
// need to load packages anyway.
case search.IsMetaPackage(path): case search.IsMetaPackage(path):
base.Errorf("go get %s: explicit requirement on standard-library module %s not allowed", path, path) base.Errorf("go get %s: explicit requirement on standard-library module %s not allowed", path, path)
continue continue
default: default:
// The argument is a package path or module path or both. // The argument is a package path.
q := &query{querySpec: querySpec{path: path, vers: vers}, arg: arg} if pkgs := modload.TargetPackages(path); len(pkgs) != 0 {
if vers == "patch" { // The path is in the main module. Nothing to query.
if *getM { if vers != "" && vers != "latest" && vers != "patch" {
for _, m := range modload.BuildList() { base.Errorf("go get %s: can't request explicit version of path in main module", arg)
if m.Path == path {
q.prevM = m
break
}
}
queries = append(queries, q)
} else {
// We need to know the module containing path before asking for
// a specific version. Wait until we load packages later.
} }
} else { continue
// The requested version of path doesn't depend on the existing version,
// so don't bother resolving it.
queries = append(queries, q)
} }
first := path
if i := strings.IndexByte(first, '/'); i >= 0 {
first = path
}
if !strings.Contains(first, ".") {
// The path doesn't have a dot in the first component and cannot be
// queried as a module. It may be a package in the standard library,
// which is fine, so don't report an error unless we encounter
// a problem loading packages below.
continue
}
// If we're querying "latest" or "patch", we need to know the current
// version of the module. For "latest", we want to avoid accidentally
// downgrading from a newer prerelease. For "patch", we need to query
// the correct minor version.
// Here, we check if "path" is the name of a module in the build list
// (other than the main module) and set prevM if so. If "path" isn't
// a module in the build list, the current version doesn't matter
// since it's either an unknown module or a package within a module
// that we'll discover later.
q := &query{querySpec: querySpec{path: path, vers: vers}, arg: arg}
if v, ok := versionByPath[path]; ok && path != modload.Target.Path {
q.prevM = module.Version{Path: path, Version: v}
q.forceModulePath = true
}
queries = append(queries, q)
} }
} }
base.ExitIfErrors() base.ExitIfErrors()
// Query modules referenced by command line arguments at requested versions, // Query modules referenced by command line arguments at requested versions.
// and add them to the build list. We need to do this before loading packages // We need to do this before loading packages since patterns that refer to
// since patterns that refer to packages in unknown modules can't be // packages in unknown modules can't be expanded. This also avoids looking
// expanded. This also avoids looking up new modules while loading packages, // up new modules while loading packages, only to downgrade later.
// only to downgrade later.
queryCache := make(map[querySpec]*query) queryCache := make(map[querySpec]*query)
byPath := runQueries(queryCache, queries, nil) byPath := runQueries(queryCache, queries, nil)
// Add queried modules to the build list. This prevents some additional // Add missing modules to the build list.
// lookups for modules at "latest" when we load packages later. // We call SetBuildList here and elsewhere, since newUpgrader,
buildList, err := mvs.UpgradeAll(modload.Target, newUpgrader(byPath, nil)) // ImportPathsQuiet, and other functions read the global build list.
for _, q := range queries {
if _, ok := versionByPath[q.m.Path]; !ok && q.m.Version != "none" {
buildList = append(buildList, q.m)
}
}
versionByPath = nil // out of date now; rebuilt later when needed
modload.SetBuildList(buildList)
// Upgrade modules specifically named on the command line. This is our only
// chance to upgrade modules without root packages (modOnly below).
// This also skips loading packages at an old version, only to upgrade
// and reload at a new version.
upgrade := make(map[string]*query)
for path, q := range byPath {
if q.path == q.m.Path && q.m.Version != "none" {
upgrade[path] = q
}
}
buildList, err := mvs.UpgradeAll(modload.Target, newUpgrader(upgrade, nil))
if err != nil { if err != nil {
base.Fatalf("go get: %v", err) base.Fatalf("go get: %v", err)
} }
@ -430,7 +441,7 @@ func runGet(cmd *base.Command, args []string) {
modOnly[q.m.Path] = q modOnly[q.m.Path] = q
continue continue
} }
if !*getM && q.path == q.m.Path { if q.path == q.m.Path {
wg.Add(1) wg.Add(1)
go func(q *query) { go func(q *query) {
if hasPkg, err := modload.ModuleHasRootPackage(q.m); err != nil { if hasPkg, err := modload.ModuleHasRootPackage(q.m); err != nil {
@ -477,17 +488,12 @@ func runGet(cmd *base.Command, args []string) {
// Don't load packages if pkgPatterns is empty. Both // Don't load packages if pkgPatterns is empty. Both
// modload.ImportPathsQuiet and ModulePackages convert an empty list // modload.ImportPathsQuiet and ModulePackages convert an empty list
// of patterns to []string{"."}, which is not what we want. // of patterns to []string{"."}, which is not what we want.
if *getM { matches = modload.ImportPathsQuiet(pkgPatterns, imports.AnyTags())
matches = modload.ModulePackages(pkgPatterns)
} else {
matches = modload.ImportPathsQuiet(pkgPatterns)
}
seenPkgs = make(map[string]bool) seenPkgs = make(map[string]bool)
install = make([]string, 0, len(pkgPatterns))
for i, match := range matches { for i, match := range matches {
arg := pkgGets[i] arg := pkgGets[i]
if !*getM && len(match.Pkgs) == 0 { if len(match.Pkgs) == 0 {
// If the pattern did not match any packages, look up a new module. // If the pattern did not match any packages, look up a new module.
// If the pattern doesn't match anything on the last iteration, // If the pattern doesn't match anything on the last iteration,
// we'll print a warning after the outer loop. // we'll print a warning after the outer loop.
@ -497,7 +503,6 @@ func runGet(cmd *base.Command, args []string) {
continue continue
} }
install = append(install, arg.path)
allStd := true allStd := true
for _, pkg := range match.Pkgs { for _, pkg := range match.Pkgs {
if !seenPkgs[pkg] { if !seenPkgs[pkg] {
@ -514,14 +519,14 @@ func runGet(cmd *base.Command, args []string) {
continue continue
} }
allStd = false allStd = false
if m.Path == modload.Target.Path {
// pkg is in the main module.
continue
}
addQuery(&query{querySpec: querySpec{path: m.Path, vers: arg.vers, forceModulePath: true, prevM: m}, arg: arg.raw}) addQuery(&query{querySpec: querySpec{path: m.Path, vers: arg.vers, forceModulePath: true, prevM: m}, arg: arg.raw})
} }
if allStd { if allStd && arg.path != arg.raw {
if *getM { base.Errorf("go get %s: cannot use pattern %q with explicit version", arg.raw, arg.raw)
base.Errorf("go get %s: cannot use pattern %q with -m", arg.raw, arg.raw)
} else if arg.path != arg.raw {
base.Errorf("go get %s: cannot use pattern %q with explicit version", arg.raw, arg.raw)
}
} }
} }
} }
@ -552,8 +557,10 @@ func runGet(cmd *base.Command, args []string) {
} }
prevBuildList = buildList prevBuildList = buildList
} }
if !*getM { if !*getD {
search.WarnUnmatched(matches) // don't warn on every iteration // Only print warnings after the last iteration,
// and only if we aren't going to build.
search.WarnUnmatched(matches)
} }
// Handle downgrades. // Handle downgrades.
@ -576,7 +583,6 @@ func runGet(cmd *base.Command, args []string) {
// Scan for any upgrades lost by the downgrades. // Scan for any upgrades lost by the downgrades.
var lostUpgrades []*query var lostUpgrades []*query
var versionByPath map[string]string
if len(down) > 0 { if len(down) > 0 {
versionByPath = make(map[string]string) versionByPath = make(map[string]string)
for _, m := range modload.BuildList() { for _, m := range modload.BuildList() {
@ -645,16 +651,19 @@ func runGet(cmd *base.Command, args []string) {
modload.AllowWriteGoMod() modload.AllowWriteGoMod()
modload.WriteGoMod() modload.WriteGoMod()
// If -m or -d was specified, we're done after the module work. We've // If -d was specified, we're done after the module work.
// already downloaded modules by loading packages above. If neither flag // We've already downloaded modules by loading packages above.
// we specified, we need build and install the packages. // Otherwise, we need to build and install the packages matched by
// Note that 'go get -u' without any arguments results in len(install) == 1: // command line arguments. This may be a different set of packages,
// search.CleanImportPaths returns "." for empty args. // since we only build packages for the target platform.
if *getM || *getD || len(install) == 0 { // Note that 'go get -u' without arguments is equivalent to
// 'go get -u .', so we'll typically build the package in the current
// directory.
if *getD || len(pkgPatterns) == 0 {
return return
} }
work.BuildInit() work.BuildInit()
pkgs := load.PackagesForBuild(install) pkgs := load.PackagesForBuild(pkgPatterns)
work.InstallPackages(install, pkgs) work.InstallPackages(install, pkgs)
} }
@ -715,18 +724,24 @@ func runQueries(cache map[querySpec]*query, queries []*query, modOnly map[string
// If forceModulePath is set, getQuery must interpret path // If forceModulePath is set, getQuery must interpret path
// as a module path. // as a module path.
func getQuery(path, vers string, prevM module.Version, forceModulePath bool) (module.Version, error) { func getQuery(path, vers string, prevM module.Version, forceModulePath bool) (module.Version, error) {
switch vers { if (prevM.Version != "") != forceModulePath {
case "": // We resolve package patterns by calling QueryPattern, which does not
vers = "latest" // accept a previous version and therefore cannot take it into account for
case "patch": // the "latest" or "patch" queries.
if prevM.Version == "" { // If we are resolving a package path or pattern, the caller has already
vers = "latest" // resolved any existing packages to their containing module(s), and
} else { // will set both prevM.Version and forceModulePath for those modules.
vers = semver.MajorMinor(prevM.Version) // The only remaining package patterns are those that are not already
} // provided by the build list, which are indicated by
// an empty prevM.Version.
base.Fatalf("go get: internal error: prevM may be set if and only if forceModulePath is set")
} }
if forceModulePath || *getM || !strings.Contains(path, "...") { if vers == "" || vers == "patch" && prevM.Version == "" {
vers = "latest"
}
if forceModulePath || !strings.Contains(path, "...") {
if path == modload.Target.Path { if path == modload.Target.Path {
if vers != "latest" { if vers != "latest" {
return module.Version{}, fmt.Errorf("can't get a specific version of the main module") return module.Version{}, fmt.Errorf("can't get a specific version of the main module")
@ -734,13 +749,13 @@ func getQuery(path, vers string, prevM module.Version, forceModulePath bool) (mo
} }
// If the path doesn't contain a wildcard, try interpreting it as a module path. // If the path doesn't contain a wildcard, try interpreting it as a module path.
info, err := modload.Query(path, vers, modload.Allowed) info, err := modload.Query(path, vers, prevM.Version, modload.Allowed)
if err == nil { if err == nil {
return module.Version{Path: path, Version: info.Version}, nil return module.Version{Path: path, Version: info.Version}, nil
} }
// If the query fails, and the path must be a real module, report the query error. // If the query fails, and the path must be a real module, report the query error.
if forceModulePath || *getM { if forceModulePath {
return module.Version{}, err return module.Version{}, err
} }
} }
@ -799,6 +814,9 @@ func newUpgrader(cmdline map[string]*query, pkgs map[string]bool) *upgrader {
work = work[1:] work = work[1:]
m := modload.PackageModule(pkg) m := modload.PackageModule(pkg)
u.upgrade[m.Path] = true u.upgrade[m.Path] = true
// testImports is empty unless test imports were actually loaded,
// i.e., -t was set or "all" was one of the arguments.
imports, testImports := modload.PackageImports(pkg) imports, testImports := modload.PackageImports(pkg)
for _, imp := range imports { for _, imp := range imports {
add(imp) add(imp)
@ -872,18 +890,14 @@ func (u *upgrader) Upgrade(m module.Version) (module.Version, error) {
} }
// Run query required by upgrade semantics. // Run query required by upgrade semantics.
// Note that query "latest" is not the same as // Note that Query "latest" is not the same as using repo.Latest,
// using repo.Latest. // which may return a pseudoversion for the latest commit.
// The query only falls back to untagged versions // Query "latest" returns the newest tagged version or the newest
// if nothing is tagged. The Latest method // prerelease version if there are no non-prereleases, or repo.Latest
// only ever returns untagged versions, // if there aren't any tagged versions. Since we're providing the previous
// which is not what we want. // version, Query will confirm the latest version is actually newer
query := "latest" // and will return the current version if not.
if getU == "patch" { info, err := modload.Query(m.Path, string(getU), m.Version, modload.Allowed)
// For patch upgrade, query "v1.2".
query = semver.MajorMinor(m.Version)
}
info, err := modload.Query(m.Path, query, modload.Allowed)
if err != nil { if err != nil {
// Report error but return m, to let version selection continue. // Report error but return m, to let version selection continue.
// (Reporting the error will fail the command at the next base.ExitIfErrors.) // (Reporting the error will fail the command at the next base.ExitIfErrors.)
@ -898,18 +912,6 @@ func (u *upgrader) Upgrade(m module.Version) (module.Version, error) {
return m, nil return m, nil
} }
// If we're on a later prerelease, keep using it,
// even though normally an Upgrade will ignore prereleases.
if semver.Compare(info.Version, m.Version) < 0 {
return m, nil
}
// If we're on a pseudo-version chronologically after the latest tagged version, keep using it.
// This avoids some accidental downgrades.
if mTime, err := modfetch.PseudoVersionTime(m.Version); err == nil && info.Time.Before(mTime) {
return m, nil
}
return module.Version{Path: m.Path, Version: info.Version}, nil return module.Version{Path: m.Path, Version: info.Version}, nil
} }

View file

@ -79,7 +79,7 @@ func addUpdate(m *modinfo.ModulePublic) {
return return
} }
if info, err := Query(m.Path, "latest", Allowed); err == nil && semver.Compare(info.Version, m.Version) > 0 { if info, err := Query(m.Path, "latest", m.Version, Allowed); err == nil && semver.Compare(info.Version, m.Version) > 0 {
m.Update = &modinfo.ModulePublic{ m.Update = &modinfo.ModulePublic{
Path: m.Path, Path: m.Path,
Version: info.Version, Version: info.Version,
@ -127,7 +127,7 @@ func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
// complete fills in the extra fields in m. // complete fills in the extra fields in m.
complete := func(m *modinfo.ModulePublic) { complete := func(m *modinfo.ModulePublic) {
if m.Version != "" { if m.Version != "" {
if q, err := Query(m.Path, m.Version, nil); err != nil { if q, err := Query(m.Path, m.Version, "", nil); err != nil {
m.Error = &modinfo.ModuleError{Err: err.Error()} m.Error = &modinfo.ModuleError{Err: err.Error()}
} else { } else {
m.Version = q.Version m.Version = q.Version

View file

@ -21,23 +21,26 @@ which source files are used in a given build.
Module support Module support
Go 1.13 includes official support for Go modules, Go 1.13 includes support for Go modules. Module-aware mode is active by default
including a module-aware 'go get' command. whenever a go.mod file is found in, or in a parent of, the current directory.
Module-aware mode is active by default.
The quickest way to take advantage of module support is to check out your
repository, create a go.mod file (described in the next section) there, and run
go commands from within that file tree.
For more fine-grained control, Go 1.13 continues to respect For more fine-grained control, Go 1.13 continues to respect
a temporary environment variable, GO111MODULE, which can be set to one a temporary environment variable, GO111MODULE, which can be set to one
of three string values: off, auto, or on (the default). of three string values: off, on, or auto (the default).
If GO111MODULE=on or is unset, then the go command requires the use of If GO111MODULE=on, then the go command requires the use of modules,
modules, never consulting GOPATH. We refer to this as the command never consulting GOPATH. We refer to this as the command
being module-aware or running in "module-aware mode". being module-aware or running in "module-aware mode".
If GO111MODULE=auto, then the go command enables or disables module
support based on the current directory. Module support is enabled only
when the current directory is outside GOPATH/src and itself contains a
go.mod file or is below a directory containing a go.mod file.
If GO111MODULE=off, then the go command never uses If GO111MODULE=off, then the go command never uses
module support. Instead it looks in vendor directories and GOPATH module support. Instead it looks in vendor directories and GOPATH
to find dependencies; we now refer to this as "GOPATH mode." to find dependencies; we now refer to this as "GOPATH mode."
If GO111MODULE=auto or is unset, then the go command enables or disables
module support based on the current directory.
Module support is enabled only when the current directory contains a
go.mod file or is below a directory containing a go.mod file.
In module-aware mode, GOPATH no longer defines the meaning of imports In module-aware mode, GOPATH no longer defines the meaning of imports
during a build, but it still stores downloaded dependencies (in GOPATH/pkg/mod) during a build, but it still stores downloaded dependencies (in GOPATH/pkg/mod)
@ -331,7 +334,9 @@ Module downloading and verification
The go command can fetch modules from a proxy or connect to source control The go command can fetch modules from a proxy or connect to source control
servers directly, according to the setting of the GOPROXY environment servers directly, according to the setting of the GOPROXY environment
variable (see 'go help env'). The default setting for GOPROXY is variable (see 'go help env'). The default setting for GOPROXY is
"https://proxy.golang.org", the Go module mirror run by Google. "https://proxy.golang.org,direct", which means to try the
Go module mirror run by Google and fall back to a direct connection
if the proxy reports that it does not have the module (HTTP error 404 or 410).
See https://proxy.golang.org/privacy for the service's privacy policy. See https://proxy.golang.org/privacy for the service's privacy policy.
If GOPROXY is set to the string "direct", downloads use a direct connection If GOPROXY is set to the string "direct", downloads use a direct connection
to source control servers. Setting GOPROXY to "off" disallows downloading to source control servers. Setting GOPROXY to "off" disallows downloading
@ -343,25 +348,15 @@ HTTP response. The string "direct" may appear in the proxy list,
to cause a direct connection to be attempted at that point in the search. to cause a direct connection to be attempted at that point in the search.
Any proxies listed after "direct" are never consulted. Any proxies listed after "direct" are never consulted.
The GONOPROXY environment variable is a comma-separated list of The GOPRIVATE and GONOPROXY environment variables allow bypassing
glob patterns (in the syntax of Go's path.Match) of module path prefixes the proxy for selected modules. See 'go help module-private' for details.
that should always be fetched directly, ignoring the GOPROXY setting.
For example,
GONOPROXY=*.corp.example.com,rsc.io/private
forces a direct connection to download modules with path prefixes matching
either pattern, including "git.corp.example.com/xyzzy", "rsc.io/private",
and "rsc.io/private/quux".
The 'go env -w' command (see 'go help env') can be used to set these variables
for future go command invocations.
No matter the source of the modules, the go command checks downloads against No matter the source of the modules, the go command checks downloads against
known checksums, to detect unexpected changes in the content of any specific known checksums, to detect unexpected changes in the content of any specific
module version from one day to the next. This check first consults the current module version from one day to the next. This check first consults the current
module's go.sum file but falls back to the Go checksum database. module's go.sum file but falls back to the Go checksum database, controlled by
See 'go help module-auth' for details. the GOSUMDB and GONOSUMDB environment variables. See 'go help module-auth'
for details.
See 'go help goproxy' for details about the proxy protocol and also See 'go help goproxy' for details about the proxy protocol and also
the format of the cached downloaded packages. the format of the cached downloaded packages.

View file

@ -18,7 +18,6 @@ import (
"cmd/go/internal/cfg" "cmd/go/internal/cfg"
"cmd/go/internal/modfetch" "cmd/go/internal/modfetch"
"cmd/go/internal/modfetch/codehost"
"cmd/go/internal/module" "cmd/go/internal/module"
"cmd/go/internal/par" "cmd/go/internal/par"
"cmd/go/internal/search" "cmd/go/internal/search"
@ -188,10 +187,13 @@ func Import(path string) (m module.Version, dir string, err error) {
candidates, err := QueryPackage(path, "latest", Allowed) candidates, err := QueryPackage(path, "latest", Allowed)
if err != nil { if err != nil {
if _, ok := err.(*codehost.VCSError); ok { if errors.Is(err, os.ErrNotExist) {
// Return "cannot find module providing package […]" instead of whatever
// low-level error QueryPackage produced.
return module.Version{}, "", &ImportMissingError{ImportPath: path}
} else {
return module.Version{}, "", err return module.Version{}, "", err
} }
return module.Version{}, "", &ImportMissingError{ImportPath: path}
} }
m = candidates[0].Mod m = candidates[0].Mod
newMissingVersion := "" newMissingVersion := ""

View file

@ -21,7 +21,7 @@ var importTests = []struct {
}, },
{ {
path: "golang.org/x/net", path: "golang.org/x/net",
err: "cannot find module providing package golang.org/x/net", err: "module golang.org/x/net@.* found, but does not contain package golang.org/x/net",
}, },
{ {
path: "golang.org/x/text", path: "golang.org/x/text",
@ -43,6 +43,7 @@ var importTests = []struct {
func TestImport(t *testing.T) { func TestImport(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
for _, tt := range importTests { for _, tt := range importTests {
t.Run(strings.ReplaceAll(tt.path, "/", "_"), func(t *testing.T) { t.Run(strings.ReplaceAll(tt.path, "/", "_"), func(t *testing.T) {

View file

@ -316,7 +316,7 @@ func InitMod() {
} }
gomod := filepath.Join(modRoot, "go.mod") gomod := filepath.Join(modRoot, "go.mod")
data, err := ioutil.ReadFile(gomod) data, err := renameio.ReadFile(gomod)
if err != nil { if err != nil {
base.Fatalf("go: %v", err) base.Fatalf("go: %v", err)
} }
@ -696,7 +696,7 @@ func WriteGoMod() {
defer unlock() defer unlock()
file := filepath.Join(modRoot, "go.mod") file := filepath.Join(modRoot, "go.mod")
old, err := ioutil.ReadFile(file) old, err := renameio.ReadFile(file)
if !bytes.Equal(old, modFileData) { if !bytes.Equal(old, modFileData) {
if bytes.Equal(old, new) { if bytes.Equal(old, new) {
// Some other process wrote the same go.mod file that we were about to write. // Some other process wrote the same go.mod file that we were about to write.
@ -739,7 +739,7 @@ func fixVersion(path, vers string) (string, error) {
return vers, nil return vers, nil
} }
info, err := Query(path, vers, nil) info, err := Query(path, vers, "", nil)
if err != nil { if err != nil {
return "", err return "", err
} }

View file

@ -55,18 +55,28 @@ func listModules(args []string, listVersions bool) []*modinfo.ModulePublic {
base.Fatalf("go: cannot use relative path %s to specify module", arg) base.Fatalf("go: cannot use relative path %s to specify module", arg)
} }
if i := strings.Index(arg, "@"); i >= 0 { if i := strings.Index(arg, "@"); i >= 0 {
info, err := Query(arg[:i], arg[i+1:], nil) path := arg[:i]
vers := arg[i+1:]
var current string
for _, m := range buildList {
if m.Path == path {
current = m.Version
break
}
}
info, err := Query(path, vers, current, nil)
if err != nil { if err != nil {
mods = append(mods, &modinfo.ModulePublic{ mods = append(mods, &modinfo.ModulePublic{
Path: arg[:i], Path: path,
Version: arg[i+1:], Version: vers,
Error: &modinfo.ModuleError{ Error: &modinfo.ModuleError{
Err: err.Error(), Err: err.Error(),
}, },
}) })
continue continue
} }
mods = append(mods, moduleInfo(module.Version{Path: arg[:i], Version: info.Version}, false)) mods = append(mods, moduleInfo(module.Version{Path: path, Version: info.Version}, false))
continue continue
} }
@ -101,11 +111,18 @@ func listModules(args []string, listVersions bool) []*modinfo.ModulePublic {
// Don't make the user provide an explicit '@latest' when they're // Don't make the user provide an explicit '@latest' when they're
// explicitly asking what the available versions are. // explicitly asking what the available versions are.
// Instead, resolve the module, even if it isn't an existing dependency. // Instead, resolve the module, even if it isn't an existing dependency.
info, err := Query(arg, "latest", nil) info, err := Query(arg, "latest", "", nil)
if err == nil { if err == nil {
mods = append(mods, moduleInfo(module.Version{Path: arg, Version: info.Version}, false)) mods = append(mods, moduleInfo(module.Version{Path: arg, Version: info.Version}, false))
continue } else {
mods = append(mods, &modinfo.ModulePublic{
Path: arg,
Error: &modinfo.ModuleError{
Err: err.Error(),
},
})
} }
continue
} }
mods = append(mods, &modinfo.ModulePublic{ mods = append(mods, &modinfo.ModulePublic{
Path: arg, Path: arg,

View file

@ -33,8 +33,7 @@ import (
// buildList is the list of modules to use for building packages. // buildList is the list of modules to use for building packages.
// It is initialized by calling ImportPaths, ImportFromFiles, // It is initialized by calling ImportPaths, ImportFromFiles,
// ModulePackages, LoadALL, or LoadBuildList, each of which uses // LoadALL, or LoadBuildList, each of which uses loaded.load.
// loaded.load.
// //
// Ideally, exactly ONE of those functions would be called, // Ideally, exactly ONE of those functions would be called,
// and exactly once. Most of the time, that's true. // and exactly once. Most of the time, that's true.
@ -52,15 +51,19 @@ var buildList []module.Version
var loaded *loader var loaded *loader
// ImportPaths returns the set of packages matching the args (patterns), // ImportPaths returns the set of packages matching the args (patterns),
// adding modules to the build list as needed to satisfy new imports. // on the target platform. Modules may be added to the build list
// to satisfy new imports.
func ImportPaths(patterns []string) []*search.Match { func ImportPaths(patterns []string) []*search.Match {
matches := ImportPathsQuiet(patterns) matches := ImportPathsQuiet(patterns, imports.Tags())
search.WarnUnmatched(matches) search.WarnUnmatched(matches)
return matches return matches
} }
// ImportPathsQuiet is like ImportPaths but does not warn about patterns with no matches. // ImportPathsQuiet is like ImportPaths but does not warn about patterns with
func ImportPathsQuiet(patterns []string) []*search.Match { // no matches. It also lets the caller specify a set of build tags to match
// packages. The build tags should typically be imports.Tags() or
// imports.AnyTags(); a nil map has no special meaning.
func ImportPathsQuiet(patterns []string, tags map[string]bool) []*search.Match {
var fsDirs [][]string var fsDirs [][]string
updateMatches := func(matches []*search.Match, iterating bool) { updateMatches := func(matches []*search.Match, iterating bool) {
for i, m := range matches { for i, m := range matches {
@ -170,81 +173,6 @@ func ImportPathsQuiet(patterns []string) []*search.Match {
} }
} }
return loadPatterns(patterns, true, updateMatches)
}
// ModulePackages returns packages provided by each module in patterns.
// patterns may contain module paths, patterns matching module paths,
// "all" (interpreted as package pattern "all"), and "." (interpreted
// as the main module). Additional modules (including modules providing
// dependencies) may be added to the build list or upgraded.
func ModulePackages(patterns []string) []*search.Match {
updateMatches := func(matches []*search.Match, iterating bool) {
for _, m := range matches {
switch {
case search.IsRelativePath(m.Pattern) || filepath.IsAbs(m.Pattern):
if m.Pattern != "." {
base.Errorf("go: path %s is not a module", m.Pattern)
continue
}
m.Pkgs = matchPackages("...", loaded.tags, false, []module.Version{Target})
case strings.Contains(m.Pattern, "..."):
match := search.MatchPattern(m.Pattern)
var matched []module.Version
for _, mod := range buildList {
if match(mod.Path) || str.HasPathPrefix(m.Pattern, mod.Path) {
matched = append(matched, mod)
}
}
m.Pkgs = matchPackages(m.Pattern, loaded.tags, false, matched)
case m.Pattern == "all":
loaded.testAll = true
if iterating {
// Enumerate the packages in the main module.
// We'll load the dependencies as we find them.
m.Pkgs = matchPackages("...", loaded.tags, false, []module.Version{Target})
} else {
// Starting with the packages in the main module,
// enumerate the full list of "all".
m.Pkgs = loaded.computePatternAll(m.Pkgs)
}
default:
found := false
for _, mod := range buildList {
if mod.Path == m.Pattern {
found = true
m.Pkgs = matchPackages("...", loaded.tags, false, []module.Version{mod})
break
}
}
if !found {
base.Errorf("go %s: module not in build list", m.Pattern)
}
}
}
}
return loadPatterns(patterns, false, updateMatches)
}
// loadPatterns returns a set of packages matching the args (patterns),
// adding modules to the build list as needed to satisfy new imports.
//
// useTags indicates whether to use the default build constraints to
// filter source files. If useTags is false, only "ignore" and malformed
// build tag requirements are considered false.
//
// The interpretation of patterns is determined by updateMatches, which will be
// called repeatedly until the build list is finalized. updateMatches should
// should store a list of matching packages in each search.Match when it is
// called. The iterating parameter is true if the build list has not been
// finalized yet.
//
// If errors are encountered, loadPatterns will print them and exit.
// On success, loadPatterns will update the build list and write go.mod.
func loadPatterns(patterns []string, useTags bool, updateMatches func(matches []*search.Match, iterating bool)) []*search.Match {
InitMod() InitMod()
var matches []*search.Match var matches []*search.Match
@ -255,10 +183,7 @@ func loadPatterns(patterns []string, useTags bool, updateMatches func(matches []
}) })
} }
loaded = newLoader() loaded = newLoader(tags)
if !useTags {
loaded.tags = anyTags
}
loaded.load(func() []string { loaded.load(func() []string {
var roots []string var roots []string
updateMatches(matches, true) updateMatches(matches, true)
@ -337,12 +262,13 @@ func warnPattern(pattern string, list []string) []string {
func ImportFromFiles(gofiles []string) { func ImportFromFiles(gofiles []string) {
InitMod() InitMod()
imports, testImports, err := imports.ScanFiles(gofiles, imports.Tags()) tags := imports.Tags()
imports, testImports, err := imports.ScanFiles(gofiles, tags)
if err != nil { if err != nil {
base.Fatalf("go: %v", err) base.Fatalf("go: %v", err)
} }
loaded = newLoader() loaded = newLoader(tags)
loaded.load(func() []string { loaded.load(func() []string {
var roots []string var roots []string
roots = append(roots, imports...) roots = append(roots, imports...)
@ -391,7 +317,7 @@ func LoadBuildList() []module.Version {
} }
func ReloadBuildList() []module.Version { func ReloadBuildList() []module.Version {
loaded = newLoader() loaded = newLoader(imports.Tags())
loaded.load(func() []string { return nil }) loaded.load(func() []string { return nil })
return buildList return buildList
} }
@ -417,9 +343,8 @@ func LoadVendor() []string {
func loadAll(testAll bool) []string { func loadAll(testAll bool) []string {
InitMod() InitMod()
loaded = newLoader() loaded = newLoader(imports.AnyTags())
loaded.isALL = true loaded.isALL = true
loaded.tags = anyTags
loaded.testAll = testAll loaded.testAll = testAll
if !testAll { if !testAll {
loaded.testRoots = true loaded.testRoots = true
@ -438,15 +363,11 @@ func loadAll(testAll bool) []string {
return paths return paths
} }
// anyTags is a special tags map that satisfies nearly all build tag expressions.
// Only "ignore" and malformed build tag requirements are considered false.
var anyTags = map[string]bool{"*": true}
// TargetPackages returns the list of packages in the target (top-level) module // TargetPackages returns the list of packages in the target (top-level) module
// matching pattern, which may be relative to the working directory, under all // matching pattern, which may be relative to the working directory, under all
// build tag settings. // build tag settings.
func TargetPackages(pattern string) []string { func TargetPackages(pattern string) []string {
return matchPackages(pattern, anyTags, false, []module.Version{Target}) return matchPackages(pattern, imports.AnyTags(), false, []module.Version{Target})
} }
// BuildList returns the module build list, // BuildList returns the module build list,
@ -589,9 +510,9 @@ type loader struct {
// LoadTests controls whether the loaders load tests of the root packages. // LoadTests controls whether the loaders load tests of the root packages.
var LoadTests bool var LoadTests bool
func newLoader() *loader { func newLoader(tags map[string]bool) *loader {
ld := new(loader) ld := new(loader)
ld.tags = imports.Tags() ld.tags = tags
ld.testRoots = LoadTests ld.testRoots = LoadTests
// Inside the "std" and "cmd" modules, we prefer to use the vendor directory // Inside the "std" and "cmd" modules, we prefer to use the vendor directory
@ -1208,11 +1129,15 @@ func (*mvsReqs) Upgrade(m module.Version) (module.Version, error) {
func versions(path string) ([]string, error) { func versions(path string) ([]string, error) {
// Note: modfetch.Lookup and repo.Versions are cached, // Note: modfetch.Lookup and repo.Versions are cached,
// so there's no need for us to add extra caching here. // so there's no need for us to add extra caching here.
repo, err := modfetch.Lookup(path) var versions []string
if err != nil { err := modfetch.TryProxies(func(proxy string) error {
return nil, err repo, err := modfetch.Lookup(proxy, path)
} if err == nil {
return repo.Versions("") versions, err = repo.Versions("")
}
return err
})
return versions, err
} }
// Previous returns the tagged version of m.Path immediately prior to // Previous returns the tagged version of m.Path immediately prior to

View file

@ -5,13 +5,15 @@
package modload package modload
import ( import (
"errors"
"fmt" "fmt"
"os"
pathpkg "path" pathpkg "path"
"strings" "strings"
"sync" "sync"
"cmd/go/internal/imports"
"cmd/go/internal/modfetch" "cmd/go/internal/modfetch"
"cmd/go/internal/modfetch/codehost"
"cmd/go/internal/module" "cmd/go/internal/module"
"cmd/go/internal/search" "cmd/go/internal/search"
"cmd/go/internal/semver" "cmd/go/internal/semver"
@ -22,22 +24,45 @@ import (
// The module must be a complete module path. // The module must be a complete module path.
// The version must take one of the following forms: // The version must take one of the following forms:
// //
// - the literal string "latest", denoting the latest available, allowed tagged version, // - the literal string "latest", denoting the latest available, allowed
// with non-prereleases preferred over prereleases. // tagged version, with non-prereleases preferred over prereleases.
// If there are no tagged versions in the repo, latest returns the most recent commit. // If there are no tagged versions in the repo, latest returns the most
// - v1, denoting the latest available tagged version v1.x.x. // recent commit.
// - v1.2, denoting the latest available tagged version v1.2.x. // - the literal string "patch", denoting the latest available tagged version
// - v1.2.3, a semantic version string denoting that tagged version. // with the same major and minor number as current. If current is "",
// - <v1.2.3, <=v1.2.3, >v1.2.3, >=v1.2.3, // "patch" is equivalent to "latest".
// denoting the version closest to the target and satisfying the given operator, // - v1, denoting the latest available tagged version v1.x.x.
// with non-prereleases preferred over prereleases. // - v1.2, denoting the latest available tagged version v1.2.x.
// - a repository commit identifier or tag, denoting that commit. // - v1.2.3, a semantic version string denoting that tagged version.
// - <v1.2.3, <=v1.2.3, >v1.2.3, >=v1.2.3,
// denoting the version closest to the target and satisfying the given operator,
// with non-prereleases preferred over prereleases.
// - a repository commit identifier or tag, denoting that commit.
// //
// If the allowed function is non-nil, Query excludes any versions for which allowed returns false. // current is optional, denoting the current version of the module.
// If query is "latest" or "patch", current will be returned if it is a newer
// semantic version or if it is a chronologically later pseudoversion. This
// prevents accidental downgrades from newer prerelease or development
// versions.
//
// If the allowed function is non-nil, Query excludes any versions for which
// allowed returns false.
// //
// If path is the path of the main module and the query is "latest", // If path is the path of the main module and the query is "latest",
// Query returns Target.Version as the version. // Query returns Target.Version as the version.
func Query(path, query string, allowed func(module.Version) bool) (*modfetch.RevInfo, error) { func Query(path, query, current string, allowed func(module.Version) bool) (*modfetch.RevInfo, error) {
var info *modfetch.RevInfo
err := modfetch.TryProxies(func(proxy string) (err error) {
info, err = queryProxy(proxy, path, query, current, allowed)
return err
})
return info, err
}
func queryProxy(proxy, path, query, current string, allowed func(module.Version) bool) (*modfetch.RevInfo, error) {
if current != "" && !semver.IsValid(current) {
return nil, fmt.Errorf("invalid previous version %q", current)
}
if allowed == nil { if allowed == nil {
allowed = func(module.Version) bool { return true } allowed = func(module.Version) bool { return true }
} }
@ -50,9 +75,22 @@ func Query(path, query string, allowed func(module.Version) bool) (*modfetch.Rev
var ok func(module.Version) bool var ok func(module.Version) bool
var prefix string var prefix string
var preferOlder bool var preferOlder bool
var mayUseLatest bool
switch { switch {
case query == "latest": case query == "latest":
ok = allowed ok = allowed
mayUseLatest = true
case query == "patch":
if current == "" {
ok = allowed
mayUseLatest = true
} else {
prefix = semver.MajorMinor(current)
ok = func(m module.Version) bool {
return matchSemverPrefix(prefix, m.Version) && allowed(m)
}
}
case strings.HasPrefix(query, "<="): case strings.HasPrefix(query, "<="):
v := query[len("<="):] v := query[len("<="):]
@ -112,14 +150,14 @@ func Query(path, query string, allowed func(module.Version) bool) (*modfetch.Rev
// If the identifier is not a canonical semver tag — including if it's a // If the identifier is not a canonical semver tag — including if it's a
// semver tag with a +metadata suffix — then modfetch.Stat will populate // semver tag with a +metadata suffix — then modfetch.Stat will populate
// info.Version with a suitable pseudo-version. // info.Version with a suitable pseudo-version.
info, err := modfetch.Stat(path, query) info, err := modfetch.Stat(proxy, path, query)
if err != nil { if err != nil {
queryErr := err queryErr := err
// The full query doesn't correspond to a tag. If it is a semantic version // The full query doesn't correspond to a tag. If it is a semantic version
// with a +metadata suffix, see if there is a tag without that suffix: // with a +metadata suffix, see if there is a tag without that suffix:
// semantic versioning defines them to be equivalent. // semantic versioning defines them to be equivalent.
if vers := module.CanonicalVersion(query); vers != "" && vers != query { if vers := module.CanonicalVersion(query); vers != "" && vers != query {
info, err = modfetch.Stat(path, vers) info, err = modfetch.Stat(proxy, path, vers)
} }
if err != nil { if err != nil {
return nil, queryErr return nil, queryErr
@ -146,7 +184,7 @@ func Query(path, query string, allowed func(module.Version) bool) (*modfetch.Rev
} }
// Load versions and execute query. // Load versions and execute query.
repo, err := modfetch.Lookup(path) repo, err := modfetch.Lookup(proxy, path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -155,41 +193,59 @@ func Query(path, query string, allowed func(module.Version) bool) (*modfetch.Rev
return nil, err return nil, err
} }
lookup := func(v string) (*modfetch.RevInfo, error) {
rev, err := repo.Stat(v)
if err != nil {
return nil, err
}
// For "latest" and "patch", make sure we don't accidentally downgrade
// from a newer prerelease or from a chronologically newer pseudoversion.
if current != "" && (query == "latest" || query == "patch") {
currentTime, err := modfetch.PseudoVersionTime(current)
if semver.Compare(rev.Version, current) < 0 || (err == nil && rev.Time.Before(currentTime)) {
return repo.Stat(current)
}
}
return rev, nil
}
if preferOlder { if preferOlder {
for _, v := range versions { for _, v := range versions {
if semver.Prerelease(v) == "" && ok(module.Version{Path: path, Version: v}) { if semver.Prerelease(v) == "" && ok(module.Version{Path: path, Version: v}) {
return repo.Stat(v) return lookup(v)
} }
} }
for _, v := range versions { for _, v := range versions {
if semver.Prerelease(v) != "" && ok(module.Version{Path: path, Version: v}) { if semver.Prerelease(v) != "" && ok(module.Version{Path: path, Version: v}) {
return repo.Stat(v) return lookup(v)
} }
} }
} else { } else {
for i := len(versions) - 1; i >= 0; i-- { for i := len(versions) - 1; i >= 0; i-- {
v := versions[i] v := versions[i]
if semver.Prerelease(v) == "" && ok(module.Version{Path: path, Version: v}) { if semver.Prerelease(v) == "" && ok(module.Version{Path: path, Version: v}) {
return repo.Stat(v) return lookup(v)
} }
} }
for i := len(versions) - 1; i >= 0; i-- { for i := len(versions) - 1; i >= 0; i-- {
v := versions[i] v := versions[i]
if semver.Prerelease(v) != "" && ok(module.Version{Path: path, Version: v}) { if semver.Prerelease(v) != "" && ok(module.Version{Path: path, Version: v}) {
return repo.Stat(v) return lookup(v)
} }
} }
} }
if query == "latest" { if mayUseLatest {
// Special case for "latest": if no tags match, use latest commit in repo, // Special case for "latest": if no tags match, use latest commit in repo,
// provided it is not excluded. // provided it is not excluded.
if info, err := repo.Latest(); err == nil && allowed(module.Version{Path: path, Version: info.Version}) { if latest, err := repo.Latest(); err == nil && allowed(module.Version{Path: path, Version: latest.Version}) {
return info, nil return lookup(latest.Version)
} }
} }
return nil, &NoMatchingVersionError{query: query} return nil, &NoMatchingVersionError{query: query, current: current}
} }
// isSemverPrefix reports whether v is a semantic version prefix: v1 or v1.2 (not v1.2.3). // isSemverPrefix reports whether v is a semantic version prefix: v1 or v1.2 (not v1.2.3).
@ -248,14 +304,14 @@ func QueryPackage(path, query string, allowed func(module.Version) bool) ([]Quer
// If any matching package is in the main module, QueryPattern considers only // If any matching package is in the main module, QueryPattern considers only
// the main module and only the version "latest", without checking for other // the main module and only the version "latest", without checking for other
// possible modules. // possible modules.
func QueryPattern(pattern string, query string, allowed func(module.Version) bool) ([]QueryResult, error) { func QueryPattern(pattern, query string, allowed func(module.Version) bool) ([]QueryResult, error) {
base := pattern base := pattern
var match func(m module.Version, root string, isLocal bool) (pkgs []string) var match func(m module.Version, root string, isLocal bool) (pkgs []string)
if i := strings.Index(pattern, "..."); i >= 0 { if i := strings.Index(pattern, "..."); i >= 0 {
base = pathpkg.Dir(pattern[:i+3]) base = pathpkg.Dir(pattern[:i+3])
match = func(m module.Version, root string, isLocal bool) []string { match = func(m module.Version, root string, isLocal bool) []string {
return matchPackages(pattern, anyTags, false, []module.Version{m}) return matchPackages(pattern, imports.AnyTags(), false, []module.Version{m})
} }
} else { } else {
match = func(m module.Version, root string, isLocal bool) []string { match = func(m module.Version, root string, isLocal bool) []string {
@ -288,6 +344,74 @@ func QueryPattern(pattern string, query string, allowed func(module.Version) boo
} }
} }
var (
results []QueryResult
candidateModules = modulePrefixesExcludingTarget(base)
)
if len(candidateModules) == 0 {
return nil, fmt.Errorf("package %s is not in the main module (%s)", pattern, Target.Path)
}
err := modfetch.TryProxies(func(proxy string) error {
queryModule := func(path string) (r QueryResult, err error) {
r.Mod.Path = path
r.Rev, err = queryProxy(proxy, path, query, "", allowed)
if err != nil {
return r, err
}
r.Mod.Version = r.Rev.Version
root, isLocal, err := fetch(r.Mod)
if err != nil {
return r, err
}
r.Packages = match(r.Mod, root, isLocal)
if len(r.Packages) == 0 {
return r, &packageNotInModuleError{
mod: r.Mod,
query: query,
pattern: pattern,
}
}
return r, nil
}
var err error
results, err = queryPrefixModules(candidateModules, queryModule)
return err
})
return results, err
}
// modulePrefixesExcludingTarget returns all prefixes of path that may plausibly
// exist as a module, excluding targetPrefix but otherwise including path
// itself, sorted by descending length.
func modulePrefixesExcludingTarget(path string) []string {
prefixes := make([]string, 0, strings.Count(path, "/")+1)
for {
if path != targetPrefix {
if _, _, ok := module.SplitPathVersion(path); ok {
prefixes = append(prefixes, path)
}
}
j := strings.LastIndexByte(path, '/')
if j < 0 {
break
}
path = path[:j]
}
return prefixes
}
type prefixResult struct {
QueryResult
err error
}
func queryPrefixModules(candidateModules []string, queryModule func(path string) (QueryResult, error)) (found []QueryResult, err error) {
// If the path we're attempting is not in the module cache and we don't have a // If the path we're attempting is not in the module cache and we don't have a
// fetch result cached either, we'll end up making a (potentially slow) // fetch result cached either, we'll end up making a (potentially slow)
// request to the proxy or (often even slower) the origin server. // request to the proxy or (often even slower) the origin server.
@ -296,141 +420,96 @@ func QueryPattern(pattern string, query string, allowed func(module.Version) boo
QueryResult QueryResult
err error err error
} }
results := make([]result, strings.Count(base, "/")+1) // by descending path length results := make([]result, len(candidateModules))
i, p := 0, base
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(len(results)) wg.Add(len(candidateModules))
for { for i, p := range candidateModules {
go func(p string, r *result) (err error) { go func(p string, r *result) {
defer func() { r.QueryResult, r.err = queryModule(p)
r.err = err wg.Done()
wg.Done()
}()
r.Mod.Path = p
if HasModRoot() && p == Target.Path {
r.Mod.Version = Target.Version
r.Rev = &modfetch.RevInfo{Version: Target.Version}
// We already know (from above) that Target does not contain any
// packages matching pattern, so leave r.Packages empty.
} else {
r.Rev, err = Query(p, query, allowed)
if err != nil {
return err
}
r.Mod.Version = r.Rev.Version
root, isLocal, err := fetch(r.Mod)
if err != nil {
return err
}
r.Packages = match(r.Mod, root, isLocal)
}
if len(r.Packages) == 0 {
return &packageNotInModuleError{
mod: r.Mod,
query: query,
pattern: pattern,
}
}
return nil
}(p, &results[i]) }(p, &results[i])
j := strings.LastIndexByte(p, '/')
if i++; i == len(results) {
if j >= 0 {
panic("undercounted slashes")
}
break
}
if j < 0 {
panic("overcounted slashes")
}
p = p[:j]
} }
wg.Wait() wg.Wait()
// Classify the results. In case of failure, identify the error that the user // Classify the results. In case of failure, identify the error that the user
// is most likely to find helpful. // is most likely to find helpful.
var ( var (
successes []QueryResult noVersion *NoMatchingVersionError
mostUseful result noPackage *packageNotInModuleError
notExistErr error
) )
for _, r := range results { for _, r := range results {
if r.err == nil { switch rErr := r.err.(type) {
successes = append(successes, r.QueryResult)
continue
}
switch mostUseful.err.(type) {
case nil: case nil:
mostUseful = r found = append(found, r.QueryResult)
continue
case *packageNotInModuleError:
// Any other error is more useful than one that reports that the main
// module does not contain the requested packages.
if mostUseful.Mod.Path == Target.Path {
mostUseful = r
continue
}
}
switch r.err.(type) {
case *codehost.VCSError:
// A VCSError means that we've located a repository, but couldn't look
// inside it for packages. That's a very strong signal, and should
// override any others.
return nil, r.err
case *packageNotInModuleError:
if r.Mod.Path == Target.Path {
// Don't override a potentially-useful error for some other module with
// a trivial error for the main module.
continue
}
// A module with an appropriate prefix exists at the requested version,
// but it does not contain the requested package(s).
if _, worsePath := mostUseful.err.(*packageNotInModuleError); !worsePath {
mostUseful = r
}
case *NoMatchingVersionError: case *NoMatchingVersionError:
// A module with an appropriate prefix exists, but not at the requested if noVersion == nil {
// version. noVersion = rErr
_, worseError := mostUseful.err.(*packageNotInModuleError) }
_, worsePath := mostUseful.err.(*NoMatchingVersionError) case *packageNotInModuleError:
if !(worseError || worsePath) { if noPackage == nil {
mostUseful = r noPackage = rErr
}
default:
if errors.Is(rErr, os.ErrNotExist) {
if notExistErr == nil {
notExistErr = rErr
}
} else {
err = r.err
} }
} }
} }
// TODO(#26232): If len(successes) == 0 and some of the errors are 4xx HTTP // TODO(#26232): If len(found) == 0 and some of the errors are 4xx HTTP
// codes, have the auth package recheck the failed paths. // codes, have the auth package recheck the failed paths.
// If we obtain new credentials for any of them, re-run the above loop. // If we obtain new credentials for any of them, re-run the above loop.
if len(successes) == 0 { if len(found) == 0 && err == nil {
// All of the possible module paths either did not exist at the requested switch {
// version, or did not contain the requested package(s). case noPackage != nil:
return nil, mostUseful.err err = noPackage
case noVersion != nil:
err = noVersion
case notExistErr != nil:
err = notExistErr
default:
panic("queryPrefixModules: no modules found, but no error detected")
}
} }
// At least one module at the requested version contained the requested return found, err
// package(s). Any remaining errors only describe the non-existence of
// alternatives, so ignore them.
return successes, nil
} }
// A NoMatchingVersionError indicates that Query found a module at the requested // A NoMatchingVersionError indicates that Query found a module at the requested
// path, but not at any versions satisfying the query string and allow-function. // path, but not at any versions satisfying the query string and allow-function.
//
// NOTE: NoMatchingVersionError MUST NOT implement Is(os.ErrNotExist).
//
// If the module came from a proxy, that proxy had to return a successful status
// code for the versions it knows about, and thus did not have the opportunity
// to return a non-400 status code to suppress fallback.
type NoMatchingVersionError struct { type NoMatchingVersionError struct {
query string query, current string
} }
func (e *NoMatchingVersionError) Error() string { func (e *NoMatchingVersionError) Error() string {
return fmt.Sprintf("no matching versions for query %q", e.query) currentSuffix := ""
if (e.query == "latest" || e.query == "patch") && e.current != "" {
currentSuffix = fmt.Sprintf(" (current version is %s)", e.current)
}
return fmt.Sprintf("no matching versions for query %q", e.query) + currentSuffix
} }
// A packageNotInModuleError indicates that QueryPattern found a candidate // A packageNotInModuleError indicates that QueryPattern found a candidate
// module at the requested version, but that module did not contain any packages // module at the requested version, but that module did not contain any packages
// matching the requested pattern. // matching the requested pattern.
//
// NOTE: packageNotInModuleError MUST NOT implement Is(os.ErrNotExist).
//
// If the module came from a proxy, that proxy had to return a successful status
// code for the versions it knows about, and thus did not have the opportunity
// to return a non-400 status code to suppress fallback.
type packageNotInModuleError struct { type packageNotInModuleError struct {
mod module.Version mod module.Version
query string query string

View file

@ -25,7 +25,7 @@ func TestMain(m *testing.M) {
} }
func testMain(m *testing.M) int { func testMain(m *testing.M) int {
modfetch.SetProxy("direct") cfg.GOPROXY = "direct"
dir, err := ioutil.TempDir("", "modload-test-") dir, err := ioutil.TempDir("", "modload-test-")
if err != nil { if err != nil {
@ -50,11 +50,12 @@ var (
) )
var queryTests = []struct { var queryTests = []struct {
path string path string
query string query string
allow string current string
vers string allow string
err string vers string
err string
}{ }{
/* /*
git init git init
@ -108,7 +109,18 @@ var queryTests = []struct {
{path: queryRepo, query: "v1.9.10-pre2+wrongmetadata", err: `unknown revision v1.9.10-pre2+wrongmetadata`}, {path: queryRepo, query: "v1.9.10-pre2+wrongmetadata", err: `unknown revision v1.9.10-pre2+wrongmetadata`},
{path: queryRepo, query: "v1.9.10-pre2", err: `unknown revision v1.9.10-pre2`}, {path: queryRepo, query: "v1.9.10-pre2", err: `unknown revision v1.9.10-pre2`},
{path: queryRepo, query: "latest", vers: "v1.9.9"}, {path: queryRepo, query: "latest", vers: "v1.9.9"},
{path: queryRepo, query: "latest", current: "v1.9.10-pre1", vers: "v1.9.10-pre1"},
{path: queryRepo, query: "latest", current: "v1.9.10-pre2+metadata", vers: "v1.9.10-pre2.0.20190513201126-42abcb6df8ee"},
{path: queryRepo, query: "latest", current: "v0.0.0-20990101120000-5ba9a4ea6213", vers: "v0.0.0-20990101120000-5ba9a4ea6213"},
{path: queryRepo, query: "latest", allow: "NOMATCH", err: `no matching versions for query "latest"`}, {path: queryRepo, query: "latest", allow: "NOMATCH", err: `no matching versions for query "latest"`},
{path: queryRepo, query: "latest", current: "v1.9.9", allow: "NOMATCH", err: `no matching versions for query "latest" (current version is v1.9.9)`},
{path: queryRepo, query: "latest", current: "v1.99.99", err: `unknown revision v1.99.99`},
{path: queryRepo, query: "patch", current: "", vers: "v1.9.9"},
{path: queryRepo, query: "patch", current: "v0.1.0", vers: "v0.1.2"},
{path: queryRepo, query: "patch", current: "v1.9.0", vers: "v1.9.9"},
{path: queryRepo, query: "patch", current: "v1.9.10-pre1", vers: "v1.9.10-pre1"},
{path: queryRepo, query: "patch", current: "v1.9.10-pre2+metadata", vers: "v1.9.10-pre2.0.20190513201126-42abcb6df8ee"},
{path: queryRepo, query: "patch", current: "v1.99.99", err: `no matching versions for query "patch" (current version is v1.99.99)`},
{path: queryRepo, query: ">v1.9.9", vers: "v1.9.10-pre1"}, {path: queryRepo, query: ">v1.9.9", vers: "v1.9.10-pre1"},
{path: queryRepo, query: ">v1.10.0", err: `no matching versions for query ">v1.10.0"`}, {path: queryRepo, query: ">v1.10.0", err: `no matching versions for query ">v1.10.0"`},
{path: queryRepo, query: ">=v1.10.0", err: `no matching versions for query ">=v1.10.0"`}, {path: queryRepo, query: ">=v1.10.0", err: `no matching versions for query ">=v1.10.0"`},
@ -136,6 +148,7 @@ var queryTests = []struct {
func TestQuery(t *testing.T) { func TestQuery(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
testenv.MustHaveExecPath(t, "git")
for _, tt := range queryTests { for _, tt := range queryTests {
allow := tt.allow allow := tt.allow
@ -146,8 +159,8 @@ func TestQuery(t *testing.T) {
ok, _ := path.Match(allow, m.Version) ok, _ := path.Match(allow, m.Version)
return ok return ok
} }
t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.query+"/"+allow, func(t *testing.T) { t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.query+"/"+tt.current+"/"+allow, func(t *testing.T) {
info, err := Query(tt.path, tt.query, allowed) info, err := Query(tt.path, tt.query, tt.current, allowed)
if tt.err != "" { if tt.err != "" {
if err != nil && err.Error() == tt.err { if err != nil && err.Error() == tt.err {
return return

View file

@ -169,6 +169,9 @@ func checkPath(path string, fileName bool) error {
if path == "" { if path == "" {
return fmt.Errorf("empty string") return fmt.Errorf("empty string")
} }
if path[0] == '-' {
return fmt.Errorf("leading dash")
}
if strings.Contains(path, "..") { if strings.Contains(path, "..") {
return fmt.Errorf("double dot") return fmt.Errorf("double dot")
} }

View file

@ -79,7 +79,7 @@ var checkPathTests = []struct {
{"/x.y/z", false, false, false}, {"/x.y/z", false, false, false},
{"x./z", false, false, false}, {"x./z", false, false, false},
{".x/z", false, false, true}, {".x/z", false, false, true},
{"-x/z", false, true, true}, {"-x/z", false, false, false},
{"x..y/z", false, false, false}, {"x..y/z", false, false, false},
{"x.y/z/../../w", false, false, false}, {"x.y/z/../../w", false, false, false},
{"x.y//z", false, false, false}, {"x.y//z", false, false, false},

View file

@ -35,7 +35,7 @@ type Reqs interface {
// Max returns the maximum of v1 and v2 (it returns either v1 or v2). // Max returns the maximum of v1 and v2 (it returns either v1 or v2).
// //
// For all versions v, Max(v, "none") must be v, // For all versions v, Max(v, "none") must be v,
// and for the tanget passed as the first argument to MVS functions, // and for the target passed as the first argument to MVS functions,
// Max(target, v) must be target. // Max(target, v) must be target.
// //
// Note that v1 < v2 can be written Max(v1, v2) != v1 // Note that v1 < v2 can be written Max(v1, v2) != v1

View file

@ -1,23 +0,0 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package renameio
import (
"os"
"syscall"
)
// isAccessDeniedError returns true if err was caused by ERROR_ACCESS_DENIED.
func isAccessDeniedError(err error) bool {
linkerr, ok := err.(*os.LinkError)
if !ok {
return false
}
errno, ok := linkerr.Err.(syscall.Errno)
if !ok {
return false
}
return errno == syscall.ERROR_ACCESS_DENIED
}

View file

@ -12,7 +12,8 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"time"
"cmd/go/internal/robustio"
) )
const patternSuffix = ".tmp" const patternSuffix = ".tmp"
@ -62,23 +63,20 @@ func WriteToFile(filename string, data io.Reader, perm os.FileMode) (err error)
return err return err
} }
var start time.Time return robustio.Rename(f.Name(), filename)
for { }
err := os.Rename(f.Name(), filename)
if err == nil || !isAccessDeniedError(err) {
return err
}
// Windows seems to occasionally trigger spurious "Access is denied" errors // ReadFile is like ioutil.ReadFile, but on Windows retries spurious errors that
// here (see golang.org/issue/31247). We're not sure why. It's probably // may occur if the file is concurrently replaced.
// worth a little extra latency to avoid propagating the spurious errors. //
if start.IsZero() { // Errors are classified heuristically and retries are bounded, so even this
start = time.Now() // function may occasionally return a spurious error on Windows.
} else if time.Since(start) >= 500*time.Millisecond { // If so, the error will likely wrap one of:
return err // - syscall.ERROR_ACCESS_DENIED
} // - syscall.ERROR_FILE_NOT_FOUND
time.Sleep(5 * time.Millisecond) // - internal/syscall/windows.ERROR_SHARING_VIOLATION
} func ReadFile(filename string) ([]byte, error) {
return robustio.ReadFile(filename)
} }
// tempFile creates a new temporary file with given permission bits. // tempFile creates a new temporary file with given permission bits.

View file

@ -2,43 +2,144 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package renameio writes files atomically by renaming temporary files. //+build !plan9
//+build !nacl,!plan9,!windows,!js
package renameio package renameio
import ( import (
"encoding/binary"
"errors"
"io/ioutil" "io/ioutil"
"math/rand"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"sync"
"sync/atomic"
"syscall" "syscall"
"testing" "testing"
"time"
"cmd/go/internal/robustio"
) )
func TestWriteFileModeAppliesUmask(t *testing.T) { func TestConcurrentReadsAndWrites(t *testing.T) {
dir, err := ioutil.TempDir("", "renameio") dir, err := ioutil.TempDir("", "renameio")
if err != nil { if err != nil {
t.Fatalf("Failed to create temporary directory: %v", err) t.Fatal(err)
}
const mode = 0644
const umask = 0007
defer syscall.Umask(syscall.Umask(umask))
file := filepath.Join(dir, "testWrite")
err = WriteFile(file, []byte("go-build"), mode)
if err != nil {
t.Fatalf("Failed to write file: %v", err)
} }
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
path := filepath.Join(dir, "blob.bin")
fi, err := os.Stat(file) const chunkWords = 8 << 10
if err != nil { buf := make([]byte, 2*chunkWords*8)
t.Fatalf("Stat %q (looking for mode %#o): %s", file, mode, err) for i := uint64(0); i < 2*chunkWords; i++ {
binary.LittleEndian.PutUint64(buf[i*8:], i)
} }
if fi.Mode()&os.ModePerm != 0640 { var attempts int64 = 128
t.Errorf("Stat %q: mode %#o want %#o", file, fi.Mode()&os.ModePerm, 0640) if !testing.Short() {
attempts *= 16
}
const parallel = 32
var sem = make(chan bool, parallel)
var (
writeSuccesses, readSuccesses int64 // atomic
writeErrnoSeen, readErrnoSeen sync.Map
)
for n := attempts; n > 0; n-- {
sem <- true
go func() {
defer func() { <-sem }()
time.Sleep(time.Duration(rand.Intn(100)) * time.Microsecond)
offset := rand.Intn(chunkWords)
chunk := buf[offset*8 : (offset+chunkWords)*8]
if err := WriteFile(path, chunk, 0666); err == nil {
atomic.AddInt64(&writeSuccesses, 1)
} else if robustio.IsEphemeralError(err) {
var (
errno syscall.Errno
dup bool
)
if errors.As(err, &errno) {
_, dup = writeErrnoSeen.LoadOrStore(errno, true)
}
if !dup {
t.Logf("ephemeral error: %v", err)
}
} else {
t.Errorf("unexpected error: %v", err)
}
time.Sleep(time.Duration(rand.Intn(100)) * time.Microsecond)
data, err := ReadFile(path)
if err == nil {
atomic.AddInt64(&readSuccesses, 1)
} else if robustio.IsEphemeralError(err) {
var (
errno syscall.Errno
dup bool
)
if errors.As(err, &errno) {
_, dup = readErrnoSeen.LoadOrStore(errno, true)
}
if !dup {
t.Logf("ephemeral error: %v", err)
}
return
} else {
t.Errorf("unexpected error: %v", err)
return
}
if len(data) != 8*chunkWords {
t.Errorf("read %d bytes, but each write is a %d-byte file", len(data), 8*chunkWords)
return
}
u := binary.LittleEndian.Uint64(data)
for i := 1; i < chunkWords; i++ {
next := binary.LittleEndian.Uint64(data[i*8:])
if next != u+1 {
t.Errorf("wrote sequential integers, but read integer out of sequence at offset %d", i)
return
}
u = next
}
}()
}
for n := parallel; n > 0; n-- {
sem <- true
}
var minWriteSuccesses int64 = attempts
if runtime.GOOS == "windows" {
// Windows produces frequent "Access is denied" errors under heavy rename load.
// As long as those are the only errors and *some* of the writes succeed, we're happy.
minWriteSuccesses = attempts / 4
}
if writeSuccesses < minWriteSuccesses {
t.Errorf("%d (of %d) writes succeeded; want ≥ %d", writeSuccesses, attempts, minWriteSuccesses)
} else {
t.Logf("%d (of %d) writes succeeded (ok: ≥ %d)", writeSuccesses, attempts, minWriteSuccesses)
}
var minReadSuccesses int64 = attempts
if runtime.GOOS == "windows" {
// Windows produces frequent "Access is denied" errors under heavy rename load.
// As long as those are the only errors and *some* of the writes succeed, we're happy.
minReadSuccesses = attempts / 4
}
if readSuccesses < minReadSuccesses {
t.Errorf("%d (of %d) reads succeeded; want ≥ %d", readSuccesses, attempts, minReadSuccesses)
} else {
t.Logf("%d (of %d) reads succeeded (ok: ≥ %d)", readSuccesses, attempts, minReadSuccesses)
} }
} }

View file

@ -0,0 +1,42 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//+build !nacl,!plan9,!windows,!js
package renameio
import (
"io/ioutil"
"os"
"path/filepath"
"syscall"
"testing"
)
func TestWriteFileModeAppliesUmask(t *testing.T) {
dir, err := ioutil.TempDir("", "renameio")
if err != nil {
t.Fatalf("Failed to create temporary directory: %v", err)
}
const mode = 0644
const umask = 0007
defer syscall.Umask(syscall.Umask(umask))
file := filepath.Join(dir, "testWrite")
err = WriteFile(file, []byte("go-build"), mode)
if err != nil {
t.Fatalf("Failed to write file: %v", err)
}
defer os.RemoveAll(dir)
fi, err := os.Stat(file)
if err != nil {
t.Fatalf("Stat %q (looking for mode %#o): %s", file, mode, err)
}
if fi.Mode()&os.ModePerm != 0640 {
t.Errorf("Stat %q: mode %#o want %#o", file, fi.Mode()&os.ModePerm, 0640)
}
}

View file

@ -0,0 +1,53 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package robustio wraps I/O functions that are prone to failure on Windows,
// transparently retrying errors up to an arbitrary timeout.
//
// Errors are classified heuristically and retries are bounded, so the functions
// in this package do not completely eliminate spurious errors. However, they do
// significantly reduce the rate of failure in practice.
//
// If so, the error will likely wrap one of:
// The functions in this package do not completely eliminate spurious errors,
// but substantially reduce their rate of occurrence in practice.
package robustio
// Rename is like os.Rename, but on Windows retries errors that may occur if the
// file is concurrently read or overwritten.
//
// (See golang.org/issue/31247 and golang.org/issue/32188.)
func Rename(oldpath, newpath string) error {
return rename(oldpath, newpath)
}
// ReadFile is like ioutil.ReadFile, but on Windows retries errors that may
// occur if the file is concurrently replaced.
//
// (See golang.org/issue/31247 and golang.org/issue/32188.)
func ReadFile(filename string) ([]byte, error) {
return readFile(filename)
}
// RemoveAll is like os.RemoveAll, but on Windows retries errors that may occur
// if an executable file in the directory has recently been executed.
//
// (See golang.org/issue/19491.)
func RemoveAll(path string) error {
return removeAll(path)
}
// IsEphemeralError reports whether err is one of the errors that the functions
// in this package attempt to mitigate.
//
// Errors considered ephemeral include:
// - syscall.ERROR_ACCESS_DENIED
// - syscall.ERROR_FILE_NOT_FOUND
// - internal/syscall/windows.ERROR_SHARING_VIOLATION
//
// This set may be expanded in the future; programs must not rely on the
// non-ephemerality of any given error.
func IsEphemeralError(err error) bool {
return isEphemeralError(err)
}

View file

@ -0,0 +1,28 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//+build !windows
package robustio
import (
"io/ioutil"
"os"
)
func rename(oldpath, newpath string) error {
return os.Rename(oldpath, newpath)
}
func readFile(filename string) ([]byte, error) {
return ioutil.ReadFile(filename)
}
func removeAll(path string) error {
return os.RemoveAll(path)
}
func isEphemeralError(err error) bool {
return false
}

View file

@ -0,0 +1,105 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package robustio
import (
"errors"
"internal/syscall/windows"
"io/ioutil"
"math/rand"
"os"
"syscall"
"time"
)
const arbitraryTimeout = 500 * time.Millisecond
// retry retries ephemeral errors from f up to an arbitrary timeout
// to work around spurious filesystem errors on Windows
func retry(f func() (err error, mayRetry bool)) error {
var (
bestErr error
lowestErrno syscall.Errno
start time.Time
nextSleep time.Duration = 1 * time.Millisecond
)
for {
err, mayRetry := f()
if err == nil || !mayRetry {
return err
}
var errno syscall.Errno
if errors.As(err, &errno) && (lowestErrno == 0 || errno < lowestErrno) {
bestErr = err
lowestErrno = errno
} else if bestErr == nil {
bestErr = err
}
if start.IsZero() {
start = time.Now()
} else if d := time.Since(start) + nextSleep; d >= arbitraryTimeout {
break
}
time.Sleep(nextSleep)
nextSleep += time.Duration(rand.Int63n(int64(nextSleep)))
}
return bestErr
}
// rename is like os.Rename, but retries ephemeral errors.
//
// It wraps os.Rename, which (as of 2019-06-04) uses MoveFileEx with
// MOVEFILE_REPLACE_EXISTING.
//
// Windows also provides a different system call, ReplaceFile,
// that provides similar semantics, but perhaps preserves more metadata. (The
// documentation on the differences between the two is very sparse.)
//
// Empirical error rates with MoveFileEx are lower under modest concurrency, so
// for now we're sticking with what the os package already provides.
func rename(oldpath, newpath string) (err error) {
return retry(func() (err error, mayRetry bool) {
err = os.Rename(oldpath, newpath)
return err, isEphemeralError(err)
})
}
// readFile is like ioutil.ReadFile, but retries ephemeral errors.
func readFile(filename string) ([]byte, error) {
var b []byte
err := retry(func() (err error, mayRetry bool) {
b, err = ioutil.ReadFile(filename)
// Unlike in rename, we do not retry ERROR_FILE_NOT_FOUND here: it can occur
// as a spurious error, but the file may also genuinely not exist, so the
// increase in robustness is probably not worth the extra latency.
return err, isEphemeralError(err) && !errors.Is(err, syscall.ERROR_FILE_NOT_FOUND)
})
return b, err
}
func removeAll(path string) error {
return retry(func() (err error, mayRetry bool) {
err = os.RemoveAll(path)
return err, isEphemeralError(err)
})
}
// isEphemeralError returns true if err may be resolved by waiting.
func isEphemeralError(err error) bool {
var errno syscall.Errno
if errors.As(err, &errno) {
switch errno {
case syscall.ERROR_ACCESS_DENIED,
syscall.ERROR_FILE_NOT_FOUND,
windows.ERROR_SHARING_VIOLATION:
return true
}
}
return false
}

View file

@ -7,9 +7,9 @@ package sumweb
import ( import (
"context" "context"
"internal/lazyregexp"
"net/http" "net/http"
"os" "os"
"regexp"
"strings" "strings"
"cmd/go/internal/tlog" "cmd/go/internal/tlog"
@ -59,7 +59,7 @@ var Paths = []string{
"/tile/", "/tile/",
} }
var modVerRE = regexp.MustCompile(`^[^@]+@v[0-9]+\.[0-9]+\.[0-9]+(-[^@]*)?(\+incompatible)?$`) var modVerRE = lazyregexp.New(`^[^@]+@v[0-9]+\.[0-9]+\.[0-9]+(-[^@]*)?(\+incompatible)?$`)
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx, err := h.Server.NewContext(r) ctx, err := h.Server.NewContext(r)

View file

@ -507,6 +507,7 @@ var testVetFlags = []string{
// "-cgocall", // "-cgocall",
// "-composites", // "-composites",
// "-copylocks", // "-copylocks",
"-errorsas",
// "-httpresponse", // "-httpresponse",
// "-lostcancel", // "-lostcancel",
// "-methods", // "-methods",

View file

@ -0,0 +1,58 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package web
import (
"errors"
"io/ioutil"
"os"
"path/filepath"
"testing"
)
func TestGetFileURL(t *testing.T) {
const content = "Hello, file!\n"
f, err := ioutil.TempFile("", "web-TestGetFileURL")
if err != nil {
t.Fatal(err)
}
if _, err := f.WriteString(content); err != nil {
t.Error(err)
}
if err := f.Close(); err != nil {
t.Fatal(err)
}
u, err := urlFromFilePath(f.Name())
if err != nil {
t.Fatal(err)
}
b, err := GetBytes(u)
if err != nil {
t.Fatalf("GetBytes(%v) = _, %v", u, err)
}
if string(b) != content {
t.Fatalf("after writing %q to %s, GetBytes(%v) read %q", content, f.Name(), u, b)
}
}
func TestGetNonexistentFile(t *testing.T) {
path, err := filepath.Abs("nonexistent")
if err != nil {
t.Fatal(err)
}
u, err := urlFromFilePath(path)
if err != nil {
t.Fatal(err)
}
b, err := GetBytes(u)
if !errors.Is(err, os.ErrNotExist) {
t.Fatalf("GetBytes(%v) = %q, %v; want _, os.ErrNotExist", u, b, err)
}
}

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