Compare commits
14 Commits
master
...
release.r6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5464bfebe7 | ||
|
|
8b228654d0 | ||
|
|
2800956682 | ||
|
|
1adba86fdd | ||
|
|
dda8180c96 | ||
|
|
1fc3d23ca0 | ||
|
|
b8f72d6110 | ||
|
|
616801951b | ||
|
|
e84d4effa1 | ||
|
|
d28f559503 | ||
|
|
f7e76c75f1 | ||
|
|
adc856a211 | ||
|
|
6a571c5191 | ||
|
|
808bb141a6 |
@@ -14,6 +14,81 @@ hg pull
|
||||
hg update release.r<i>NN</i>
|
||||
</pre>
|
||||
|
||||
<h2 id="r60">r60 (released 2011/09/07)</h2>
|
||||
|
||||
<p>
|
||||
The r60 release corresponds to
|
||||
<code><a href="weekly.html#2011-08-17">weekly.2011-08-17</a></code>.
|
||||
This section highlights the most significant changes in this release.
|
||||
For a more detailed summary, see the
|
||||
<a href="weekly.html#2011-08-17">weekly release notes</a>.
|
||||
For complete information, see the
|
||||
<a href="http://code.google.com/p/go/source/list?r=release-branch.r60">Mercurial change list</a>.
|
||||
</p>
|
||||
|
||||
<h3 id="r60.lang">Language</h3>
|
||||
|
||||
<p>
|
||||
An "else" block is now required to have braces except if the body of the "else"
|
||||
is another "if". Since gofmt always puts those braces in anyway,
|
||||
gofmt-formatted programs will not be affected.
|
||||
To fix other programs, run gofmt.
|
||||
</p>
|
||||
|
||||
<h3 id="r60.pkg">Packages</h3>
|
||||
|
||||
<p>
|
||||
<a href="/pkg/http/">Package http</a>'s URL parsing and query escaping code
|
||||
(such as <code>ParseURL</code> and <code>URLEscape</code>) has been moved to
|
||||
the new <a href="/pkg/url/">url package</a>, with several simplifications to
|
||||
the names. Client code can be updated automatically with gofix.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a href="/pkg/image/">Package image</a> has had significant changes made to the
|
||||
<code>Pix</code> field of struct types such as
|
||||
<a href="/pkg/image/#RGBA">image.RGBA</a> and
|
||||
<a href="/pkg/image/#NRGBA">image.NRGBA</a>.
|
||||
The <a href="/pkg/image/#Image">image.Image</a> interface type has not changed,
|
||||
though, and you should not need to change your code if you don't explicitly
|
||||
refer to <code>Pix</code> fields. For example, if you decode a number of images
|
||||
using the <a href="/pkg/image/jpeg/">image/jpeg</a> package, compose them using
|
||||
<a href="/pkg/image/draw/">image/draw</a>, and then encode the result using
|
||||
<a href="/pkg/img/png">image/png</a>, then your code should still work as
|
||||
before.
|
||||
If your code <i>does</i> refer to <code>Pix</code> fields see the
|
||||
<a href="/doc/devel/weekly.html#2011-07-19">weekly.2011-07-19</a>
|
||||
snapshot notes for how to update your code.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a href="/pkg/template/">Package template</a> has been replaced with a new
|
||||
templating package (formerly <code>exp/template</code>). The original template
|
||||
package is still available as <a href="/pkg/old/template/">old/template</a>.
|
||||
The <code>old/template</code> package is deprecated and will be removed.
|
||||
The Go tree has been updated to use the new template package. We encourage
|
||||
users of the old template package to switch to the new one. Code that uses
|
||||
<code>template</code> or <code>exp/template</code> will need to change its
|
||||
import lines to <code>"old/template"</code> or <code>"template"</code>,
|
||||
respectively.
|
||||
</p>
|
||||
|
||||
<h3 id="r60.cmd">Tools</h3>
|
||||
|
||||
<p>
|
||||
<a href="/cmd/goinstall/">Goinstall</a> now uses a new tag selection scheme.
|
||||
When downloading or updating, goinstall looks for a tag or branch with the
|
||||
<code>"go."</code> prefix that corresponds to the local Go version. For Go
|
||||
<code>release.r58</code> it looks for <code>go.r58</code>. For
|
||||
<code>weekly.2011-06-03</code> it looks for <code>go.weekly.2011-06-03</code>.
|
||||
If the specific <code>go.X</code> tag or branch is not found, it chooses the
|
||||
closest earlier version. If an appropriate tag or branch is found, goinstall
|
||||
uses that version of the code. Otherwise it uses the default version selected
|
||||
by the version control system. Library authors are encouraged to use the
|
||||
appropriate tag or branch names in their repositories to make their libraries
|
||||
more accessible.
|
||||
</p>
|
||||
|
||||
<h2 id="r59">r59 (released 2011/08/01)</h2>
|
||||
|
||||
<p>
|
||||
@@ -323,7 +398,7 @@ Remember that gofix will handle the bulk of the rewrites
|
||||
necessary for these changes to package APIs.
|
||||
</p>
|
||||
|
||||
<h3 id="r57.tool">Tools</h3>
|
||||
<h3 id="r57.cmd">Tools</h3>
|
||||
|
||||
<p><a href="/cmd/gofix/">Gofix</a>, a new command, is described above.</p>
|
||||
|
||||
|
||||
@@ -14,7 +14,195 @@ hg pull
|
||||
hg update weekly.<i>YYYY-MM-DD</i>
|
||||
</pre>
|
||||
|
||||
<h2 id="2011-08-17">2011-08-17</h2>
|
||||
<h2 id="2011-09-07">2011-09-07</h2>
|
||||
|
||||
<pre>
|
||||
This weekly snapshot consists of improvements and bug fixes, including fixes
|
||||
for issues introduced by escape analysis changes in the gc compiler.
|
||||
|
||||
* build: clear execute bit from Go files (thanks Mike Rosset),
|
||||
error out if problem with sudo.bash /usr/local/bin (thanks Mike Rosset).
|
||||
* exp/norm: add Reader and Writer,
|
||||
performance improvements of quickSpan.
|
||||
* exp/regexp: bug fixes and RE2 tests.
|
||||
* exp/template/html: string replacement refactoring,
|
||||
tweaks to js{,_test}.go.
|
||||
* gc: add -p flag to catch import cycles earlier,
|
||||
fix label recursion bugs,
|
||||
fix zero-length struct eval,
|
||||
zero stack-allocated slice backing arrays,
|
||||
* gc, ld: fix Windows file paths (thanks Hector Chu).
|
||||
* go/parser: accept corner cases of signature syntax.
|
||||
* gobuilder: ignore _test.go files when looking for docs, more logging.
|
||||
* godoc: minor tweaks for App Engine use.
|
||||
* gofix: do not convert url in field names (thanks Gustavo Niemeyer).
|
||||
* gofmt: indent multi-line signatures.
|
||||
* gopprof: regexp fixes (thanks Hector Chu).
|
||||
* image/png: check zlib checksum during Decode.
|
||||
* libmach: fix incorrect use of memset (thanks Dave Cheney).
|
||||
* misc/goplay: fix template output.
|
||||
* net: ParseCIDR returns IPNet instead of IPMask (thanks Mikio Hara),
|
||||
sync CIDRMask code, doc.
|
||||
* os: use GetFileAttributesEx to implement Stat on windows (thanks Alex Brainman).
|
||||
* runtime: fix openbsd 386 raisesigpipe,
|
||||
implement exception handling on windows/amd64 (thanks Hector Chu),
|
||||
test for concurrent channel consumers (thanks Christopher Wedgwood).
|
||||
* sort: use heapsort to bail out quicksort (thanks Ziad Hatahet).
|
||||
* sync/atomic: add LoadUintptr, add Store functions.
|
||||
* syscall: update routing message attributes handling (thanks Mikio Hara).
|
||||
* template: fix deadlock,
|
||||
indirect or dereference function arguments if necessary,
|
||||
slightly simplify the test for assignability of arguments.
|
||||
* url: handle ; in ParseQuery.
|
||||
* websocket: fix incorrect prints found by govet (thanks Robert Hencke).
|
||||
</pre>
|
||||
|
||||
<h2 id="2011-09-01">2011-09-01</h2>
|
||||
|
||||
<pre>
|
||||
This weekly contains performance improvements and bug fixes.
|
||||
|
||||
The gc compiler now does escape analysis, which improves program performance
|
||||
by placing variables on the call stack instead of the heap when it is safe to
|
||||
do so.
|
||||
|
||||
The container/vector package is deprecated and will be removed at some point
|
||||
in the future.
|
||||
|
||||
Other changes:
|
||||
* archive/tar: support symlinks. (thanks Mike Rosset)
|
||||
* big: fix nat.scan bug. (thanks Evan Shaw)
|
||||
* bufio: handle a "\r\n" that straddles the buffer.
|
||||
add openbsd.
|
||||
avoid redundant bss declarations.
|
||||
fix unused parameters.
|
||||
fix windows/amd64 build with newest mingw-w64. (thanks Hector Chu)
|
||||
* bytes: clarify that NewBuffer is not for beginners.
|
||||
* cgo: explain how to free something.
|
||||
fix GoBytes. (thanks Gustavo Niemeyer)
|
||||
fixes callback for windows amd64. (thanks Wei Guangjing)
|
||||
note that CString result must be freed. (thanks Gustavo Niemeyer)
|
||||
* cov: remove tautological #defines. (thanks Lucio De Re)
|
||||
* dashboard: yet another utf-8 fix.
|
||||
* doc/codelab/wiki: fix Makefile.
|
||||
* doc/progs: fix windows/amd64. (thanks Jaroslavas Počepko)
|
||||
* doc/tmpltohtml: update to new template package.
|
||||
* doc: emphasize that environment variables are optional.
|
||||
* effective_go: convert to use tmpltohtml.
|
||||
* exp/norm: reduced the size of the byte buffer used by reorderBuffer by half by reusing space when combining.
|
||||
a few minor fixes to support the implementation of norm.
|
||||
added implementation for []byte versions of methods.
|
||||
* exp/template/html: add some tests for ">" attributes.
|
||||
added handling for URL attributes.
|
||||
differentiate URL-valued attributes (such as href).
|
||||
reworked escapeText to recognize attr boundaries.
|
||||
* exp/wingui: made compatible with windows/amd64. (thanks Jaroslavas Počepko)
|
||||
* flag: add Parsed, restore Usage.
|
||||
* gc: add openbsd.
|
||||
escape analysis.
|
||||
fix build on Plan 9. (thanks Lucio De Re)
|
||||
fix div bug.
|
||||
fix pc/line table. (thanks Julian Phillips)
|
||||
fix some spurious leaks.
|
||||
make static initialization more static.
|
||||
remove JCXZ; add JCXZW, JCXZL, and JCXZQ instructions. (thanks Jaroslavas Počepko)
|
||||
shuffle #includes.
|
||||
simplify escape analysis recursion.
|
||||
tweak and enable escape analysis.
|
||||
* go/ast cleanup: base File/PackageExports on FilterFile/FilterPackage code.
|
||||
adjustments to filter function.
|
||||
fix ast.MergePackageFiles to collect infos about imports. (thanks Sebastien Binet)
|
||||
generalize ast.FilterFile.
|
||||
* go/build: add test support & use in gotest.
|
||||
separate test imports out when scanning. (thanks Gustavo Niemeyer)
|
||||
* go/parser: fix type switch scoping.
|
||||
fix type switch scoping.
|
||||
* gob: explain that Debug isn't useful unless it's compiled in.
|
||||
* gobuilder: increase log limit.
|
||||
* godashboard: fix utf-8 in user names.
|
||||
* godoc: first step towards reducing index size.
|
||||
add dummy playground.js to silence godoc warning at start-up.
|
||||
added systematic throttling to indexing goroutine.
|
||||
fix bug in zip.go.
|
||||
support for reading/writing (splitted) index files.
|
||||
use virtual file system when generating package synopses.
|
||||
* gofix: forgot to rename the URL type.
|
||||
osopen: fixed=true when changing O_CREAT. (thanks Tarmigan Casebolt)
|
||||
* goinstall: error out with paths that end with '/'. (thanks Tarmigan Casebolt)
|
||||
report lack of $GOPATH on errors. (thanks Gustavo Niemeyer)
|
||||
select the tag that is closest to runtime.Version.
|
||||
* gotry: add missing $. (thanks Tarmigan Casebolt)
|
||||
* http: add MaxBytesReader to limit request body size.
|
||||
add file protocol transport.
|
||||
adjust test threshold for larger suse buffers.
|
||||
delete error kludge.
|
||||
on invalid request, send 400 response.
|
||||
return 413 instead of 400 when the request body is too large. (thanks Dave Cheney)
|
||||
support setting Transport's TLS client config.
|
||||
* image/tiff: add a decode benchmark. (thanks Benny Siegert)
|
||||
decoder optimization. (thanks Benny Siegert)
|
||||
* image: add PalettedImage interface, and make image/png recognize it. (thanks Jaroslavas Počepko)
|
||||
* io: add TeeReader. (thanks Hector Chu)
|
||||
* json: add struct tag option to wrap literals in strings.
|
||||
calculate Offset for Indent correctly. (thanks Jeff Hodges)
|
||||
fix decode bug with struct tag names with ,opts being ignored.
|
||||
* ld: handle Plan 9 ar format. (thanks Lucio De Re)
|
||||
remove duplicate bss definitions.
|
||||
* libmach: support reading symbols from Windows .exe for nm. (thanks Mateusz Czapliński)
|
||||
* math: fix Pow10 loop. (thanks Volker Dobler)
|
||||
* mime: ParseMediaType returns os.Error now, not a nil map.
|
||||
media type formatter. (thanks Pascal S. de Kloe)
|
||||
text charset defaults. (thanks Pascal S. de Kloe)
|
||||
* misc/dashboard: remove limit for json package list.
|
||||
* misc/emacs: refine label detection.
|
||||
* net: add ParseMAC function. (thanks Paul Borman)
|
||||
change the internal form of IPMask for IPv4. (thanks Mikio Hara)
|
||||
disable "tcp" test on openbsd.
|
||||
fix windows build. (thanks Alex Brainman)
|
||||
join and leave a IPv6 group address, on a specific interface. (thanks Mikio Hara)
|
||||
make use of IPv4len, IPv6len. (thanks Mikio Hara)
|
||||
move internal string manipulation routines to parse.go. (thanks Mikio Hara)
|
||||
* os: disable Hostname test on OpenBSD.
|
||||
fix WNOHANG Waitmsg. (thanks Gustavo Niemeyer)
|
||||
* reflect: add Value.Bytes, Value.SetBytes methods.
|
||||
* rpc: add benchmark for async rpc calls.
|
||||
* runtime: add openbsd 386 defs.h.
|
||||
add runtime support for openbsd 386.
|
||||
add runtime· prefix to showframe.
|
||||
ctrlhandler for windows amd64. (thanks Wei Guangjing)
|
||||
fix stack cleanup on windows/amd64. (thanks Hector Chu)
|
||||
fix void warnings.
|
||||
go interface to cdecl calbacks. (thanks Jaroslavas Počepko)
|
||||
handle string + char literals in goc2c.
|
||||
make arm work on Ubuntu Natty qemu.
|
||||
openbsd thread tweaks.
|
||||
simplify stack traces.
|
||||
speed up cgo calls. (thanks Alex Brainman)
|
||||
use cgo runtime functions to call windows syscalls. (thanks Alex Brainman)
|
||||
windows/amd64 callbacks fixed and syscall fixed to allow using it in callbacks. (thanks Jaroslavas Počepko)
|
||||
* strconv: put decimal on stack.
|
||||
* spec: update section on Implementation Differences.
|
||||
* syscall: SOMAXCONN should be 0x7fffffff at winsock2. (thanks Yasuhiro Matsumoto)
|
||||
add openbsd 386.
|
||||
handle RTM_NEWROUTE in ParseNetlinkRouteAttr on Linux. (thanks Albert Strasheim)
|
||||
handle routing entry in ParseRoutingSockaddr on BSD variants. (thanks Mikio Hara)
|
||||
openbsd amd64 syscall support.
|
||||
use the vdso page on linux x86 for faster syscalls instead of int $0x80. (thanks Yuval Pavel Zholkover)
|
||||
* template/parse: give if, range, and with a common representation.
|
||||
* template: grammar fix for template documentation. (thanks Bill Neubauer)
|
||||
range over channel.
|
||||
remove else and end nodes from public view.
|
||||
* test: put GOROOT/bin before all others in run.
|
||||
* time: fix Plan 9 build. (thanks Fazlul Shahriar)
|
||||
fix zone during windows test.
|
||||
* type switches: test for pathological case.
|
||||
* version.bash: update VERSION on -save if already present. (thanks Gustavo Niemeyer)
|
||||
* websocket: implements new version of WebSocket protocol. (thanks Fumitoshi Ukai)
|
||||
* windows/386: clean stack after syscall. (thanks Jaroslavas Počepko)
|
||||
* xml: marshal "parent>child" tags correctly. (thanks Ross Light)
|
||||
</pre>
|
||||
|
||||
<h2 id="2011-08-17">2011-08-17 (<a href="release.html#r60">base for r60</a>)</h2>
|
||||
|
||||
<pre>
|
||||
This weekly contains some package re-shuffling. Users of the http and
|
||||
|
||||
@@ -127,6 +127,7 @@ static void
|
||||
outhist(Biobuf *b)
|
||||
{
|
||||
Hist *h;
|
||||
int i, depth = 0;
|
||||
char *p, ds[] = {'c', ':', '/', 0};
|
||||
|
||||
for(h = hist; h != H; h = h->link) {
|
||||
@@ -156,13 +157,21 @@ outhist(Biobuf *b)
|
||||
outzfile(b, p+1);
|
||||
} else {
|
||||
// relative name, like dir/file.go
|
||||
if(h->offset == 0 && pathname && pathname[0] == '/') {
|
||||
if(h->offset >= 0 && pathname && pathname[0] == '/') {
|
||||
zfile(b, "/", 1); // leading "/"
|
||||
outzfile(b, pathname+1);
|
||||
}
|
||||
outzfile(b, p);
|
||||
}
|
||||
}
|
||||
if(h->offset > 0) {
|
||||
//line directive
|
||||
depth++;
|
||||
}
|
||||
} else if(depth > 0) {
|
||||
for(i = 0; i < depth; i++)
|
||||
zhist(b, h->line, h->offset);
|
||||
depth = 0;
|
||||
}
|
||||
zhist(b, h->line, h->offset);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ func osopen(f *ast.File) bool {
|
||||
// Rename O_CREAT to O_CREATE.
|
||||
if expr, ok := n.(ast.Expr); ok && isPkgDot(expr, "os", "O_CREAT") {
|
||||
expr.(*ast.SelectorExpr).Sel.Name = "O_CREATE"
|
||||
fixed = true
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,29 @@ func f() {
|
||||
os.OpenFile(a, os.O_SURPRISE|os.O_CREATE, 0666)
|
||||
_ = os.O_CREATE
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
Name: "osopen.1",
|
||||
In: `package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func f() {
|
||||
_ = os.O_CREAT
|
||||
}
|
||||
`,
|
||||
Out: `package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func f() {
|
||||
_ = os.O_CREATE
|
||||
}
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ func init() {
|
||||
}
|
||||
|
||||
var urlRenames = []struct{ in, out string }{
|
||||
{"URL", "URL"},
|
||||
{"ParseURL", "Parse"},
|
||||
{"ParseURLReference", "ParseWithReference"},
|
||||
{"ParseQuery", "ParseQuery"},
|
||||
@@ -45,7 +46,12 @@ func url(f *ast.File) bool {
|
||||
fixed := false
|
||||
|
||||
// Update URL code.
|
||||
var skip interface{}
|
||||
urlWalk := func(n interface{}) {
|
||||
if n == skip {
|
||||
skip = nil
|
||||
return
|
||||
}
|
||||
// Is it an identifier?
|
||||
if ident, ok := n.(*ast.Ident); ok && ident.Name == "url" {
|
||||
ident.Name = "url_"
|
||||
@@ -56,6 +62,12 @@ func url(f *ast.File) bool {
|
||||
fixed = urlDoFields(fn.Params) || fixed
|
||||
fixed = urlDoFields(fn.Results) || fixed
|
||||
}
|
||||
// U{url: ...} is likely a struct field.
|
||||
if kv, ok := n.(*ast.KeyValueExpr); ok {
|
||||
if ident, ok := kv.Key.(*ast.Ident); ok && ident.Name == "url" {
|
||||
skip = ident
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fix up URL code and add import, at most once.
|
||||
@@ -63,7 +75,7 @@ func url(f *ast.File) bool {
|
||||
if fixed {
|
||||
return
|
||||
}
|
||||
walk(f, urlWalk)
|
||||
walkBeforeAfter(f, urlWalk, nop)
|
||||
addImport(f, "url")
|
||||
fixed = true
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
)
|
||||
|
||||
func f() {
|
||||
var _ http.URL
|
||||
http.ParseURL(a)
|
||||
http.ParseURLReference(a)
|
||||
http.ParseQuery(a)
|
||||
@@ -33,6 +34,7 @@ func f() {
|
||||
import "url"
|
||||
|
||||
func f() {
|
||||
var _ url.URL
|
||||
url.Parse(a)
|
||||
url.ParseWithReference(a)
|
||||
url.ParseQuery(a)
|
||||
@@ -78,10 +80,15 @@ import (
|
||||
"http"
|
||||
)
|
||||
|
||||
type U struct{ url int }
|
||||
type M map[int]int
|
||||
|
||||
func f() {
|
||||
http.ParseURL(a)
|
||||
var url = 23
|
||||
url, x := 45, y
|
||||
_ = U{url: url}
|
||||
_ = M{url + 1: url}
|
||||
}
|
||||
|
||||
func g(url string) string {
|
||||
@@ -96,10 +103,15 @@ func h() (url string) {
|
||||
|
||||
import "url"
|
||||
|
||||
type U struct{ url int }
|
||||
type M map[int]int
|
||||
|
||||
func f() {
|
||||
url.Parse(a)
|
||||
var url_ = 23
|
||||
url_, x := 45, y
|
||||
_ = U{url: url_}
|
||||
_ = M{url_ + 1: url_}
|
||||
}
|
||||
|
||||
func g(url_ string) string {
|
||||
|
||||
@@ -11,3 +11,9 @@ GOFILES=\
|
||||
make.go\
|
||||
|
||||
include ../../Make.cmd
|
||||
|
||||
test:
|
||||
gotest
|
||||
|
||||
testshort:
|
||||
gotest -test.short
|
||||
|
||||
@@ -94,8 +94,11 @@ attempt to fetch updates. The -u flag changes this behavior,
|
||||
causing goinstall to update all remote packages encountered during
|
||||
the installation.
|
||||
|
||||
When downloading or updating, goinstall first looks for a tag or branch
|
||||
named "release". If there is one, it uses that version of the code.
|
||||
When downloading or updating, goinstall looks for a tag with the "go." prefix
|
||||
that corresponds to the local Go version. For Go "release.r58" it looks for a
|
||||
tag named "go.r58". For "weekly.2011-06-03" it looks for "go.weekly.2011-06-03".
|
||||
If the specific "go.X" tag is not found, it chooses the closest earlier version.
|
||||
If an appropriate tag is found, goinstall uses that version of the code.
|
||||
Otherwise it uses the default version selected by the version control
|
||||
system, typically HEAD for git, tip for Mercurial.
|
||||
|
||||
|
||||
@@ -7,12 +7,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"exec"
|
||||
"fmt"
|
||||
"http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -36,22 +39,21 @@ func maybeReportToDashboard(path string) {
|
||||
// a vcs represents a version control system
|
||||
// like Mercurial, Git, or Subversion.
|
||||
type vcs struct {
|
||||
name string
|
||||
cmd string
|
||||
metadir string
|
||||
checkout string
|
||||
clone string
|
||||
update string
|
||||
updateReleaseFlag string
|
||||
pull string
|
||||
pullForceFlag string
|
||||
log string
|
||||
logLimitFlag string
|
||||
logReleaseFlag string
|
||||
check string
|
||||
protocols []string
|
||||
suffix string
|
||||
defaultHosts []host
|
||||
name string
|
||||
cmd string
|
||||
metadir string
|
||||
checkout string
|
||||
clone string
|
||||
update string
|
||||
updateRevFlag string
|
||||
pull string
|
||||
pullForceFlag string
|
||||
tagList string
|
||||
tagListRe *regexp.Regexp
|
||||
check string
|
||||
protocols []string
|
||||
suffix string
|
||||
defaultHosts []host
|
||||
}
|
||||
|
||||
type host struct {
|
||||
@@ -61,20 +63,18 @@ type host struct {
|
||||
}
|
||||
|
||||
var hg = vcs{
|
||||
name: "Mercurial",
|
||||
cmd: "hg",
|
||||
metadir: ".hg",
|
||||
checkout: "checkout",
|
||||
clone: "clone",
|
||||
update: "update",
|
||||
updateReleaseFlag: "release",
|
||||
pull: "pull",
|
||||
log: "log",
|
||||
logLimitFlag: "-l1",
|
||||
logReleaseFlag: "-rrelease",
|
||||
check: "identify",
|
||||
protocols: []string{"https", "http"},
|
||||
suffix: ".hg",
|
||||
name: "Mercurial",
|
||||
cmd: "hg",
|
||||
metadir: ".hg",
|
||||
checkout: "checkout",
|
||||
clone: "clone",
|
||||
update: "update",
|
||||
pull: "pull",
|
||||
tagList: "tags",
|
||||
tagListRe: regexp.MustCompile("([^ ]+)[^\n]+\n"),
|
||||
check: "identify",
|
||||
protocols: []string{"https", "http"},
|
||||
suffix: ".hg",
|
||||
defaultHosts: []host{
|
||||
{regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/hg)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""},
|
||||
{regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http", ""},
|
||||
@@ -82,20 +82,18 @@ var hg = vcs{
|
||||
}
|
||||
|
||||
var git = vcs{
|
||||
name: "Git",
|
||||
cmd: "git",
|
||||
metadir: ".git",
|
||||
checkout: "checkout",
|
||||
clone: "clone",
|
||||
update: "pull",
|
||||
updateReleaseFlag: "release",
|
||||
pull: "fetch",
|
||||
log: "show-ref",
|
||||
logLimitFlag: "",
|
||||
logReleaseFlag: "release",
|
||||
check: "ls-remote",
|
||||
protocols: []string{"git", "https", "http"},
|
||||
suffix: ".git",
|
||||
name: "Git",
|
||||
cmd: "git",
|
||||
metadir: ".git",
|
||||
checkout: "checkout",
|
||||
clone: "clone",
|
||||
update: "pull",
|
||||
pull: "fetch",
|
||||
tagList: "tag",
|
||||
tagListRe: regexp.MustCompile("([^\n]+)\n"),
|
||||
check: "ls-remote",
|
||||
protocols: []string{"git", "https", "http"},
|
||||
suffix: ".git",
|
||||
defaultHosts: []host{
|
||||
{regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/git)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""},
|
||||
{regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http", ".git"},
|
||||
@@ -103,40 +101,35 @@ var git = vcs{
|
||||
}
|
||||
|
||||
var svn = vcs{
|
||||
name: "Subversion",
|
||||
cmd: "svn",
|
||||
metadir: ".svn",
|
||||
checkout: "checkout",
|
||||
clone: "checkout",
|
||||
update: "update",
|
||||
updateReleaseFlag: "release",
|
||||
log: "log",
|
||||
logLimitFlag: "-l1",
|
||||
logReleaseFlag: "release",
|
||||
check: "info",
|
||||
protocols: []string{"https", "http", "svn"},
|
||||
suffix: ".svn",
|
||||
name: "Subversion",
|
||||
cmd: "svn",
|
||||
metadir: ".svn",
|
||||
checkout: "checkout",
|
||||
clone: "checkout",
|
||||
update: "update",
|
||||
check: "info",
|
||||
protocols: []string{"https", "http", "svn"},
|
||||
suffix: ".svn",
|
||||
defaultHosts: []host{
|
||||
{regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/svn)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""},
|
||||
},
|
||||
}
|
||||
|
||||
var bzr = vcs{
|
||||
name: "Bazaar",
|
||||
cmd: "bzr",
|
||||
metadir: ".bzr",
|
||||
checkout: "update",
|
||||
clone: "branch",
|
||||
update: "update",
|
||||
updateReleaseFlag: "-rrelease",
|
||||
pull: "pull",
|
||||
pullForceFlag: "--overwrite",
|
||||
log: "log",
|
||||
logLimitFlag: "-l1",
|
||||
logReleaseFlag: "-rrelease",
|
||||
check: "info",
|
||||
protocols: []string{"https", "http", "bzr"},
|
||||
suffix: ".bzr",
|
||||
name: "Bazaar",
|
||||
cmd: "bzr",
|
||||
metadir: ".bzr",
|
||||
checkout: "update",
|
||||
clone: "branch",
|
||||
update: "update",
|
||||
updateRevFlag: "-r",
|
||||
pull: "pull",
|
||||
pullForceFlag: "--overwrite",
|
||||
tagList: "tags",
|
||||
tagListRe: regexp.MustCompile("([^ ]+)[^\n]+\n"),
|
||||
check: "info",
|
||||
protocols: []string{"https", "http", "bzr"},
|
||||
suffix: ".bzr",
|
||||
defaultHosts: []host{
|
||||
{regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+))(/[a-z0-9A-Z_.\-/]+)?$`), "https", ""},
|
||||
},
|
||||
@@ -240,20 +233,84 @@ func download(pkg, srcDir string) (public bool, err os.Error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Try to detect if a "release" tag exists. If it does, update
|
||||
// to the tagged version, otherwise just update the current branch.
|
||||
// NOTE(_nil): svn will always fail because it is trying to get
|
||||
// the revision history of a file named "release" instead of
|
||||
// looking for a commit with a release tag
|
||||
// updateRepo gets a list of tags in the repository and
|
||||
// checks out the tag closest to the current runtime.Version.
|
||||
// If no matching tag is found, it just updates to tip.
|
||||
func (v *vcs) updateRepo(dst string) os.Error {
|
||||
if err := quietRun(dst, nil, v.cmd, v.log, v.logLimitFlag, v.logReleaseFlag); err == nil {
|
||||
if err := run(dst, nil, v.cmd, v.checkout, v.updateReleaseFlag); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err := run(dst, nil, v.cmd, v.update); err != nil {
|
||||
if v.tagList == "" || v.tagListRe == nil {
|
||||
// TODO(adg): fix for svn
|
||||
return run(dst, nil, v.cmd, v.update)
|
||||
}
|
||||
|
||||
// Get tag list.
|
||||
stderr := new(bytes.Buffer)
|
||||
cmd := exec.Command(v.cmd, v.tagList)
|
||||
cmd.Dir = dst
|
||||
cmd.Stderr = stderr
|
||||
b, err := cmd.Output()
|
||||
if err != nil {
|
||||
errorf("%s %s: %s\n", v.cmd, v.tagList, stderr)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
var tags []string
|
||||
for _, m := range v.tagListRe.FindAllStringSubmatch(string(b), -1) {
|
||||
tags = append(tags, m[1])
|
||||
}
|
||||
|
||||
// Only use the tag component of runtime.Version.
|
||||
ver := strings.Split(runtime.Version(), " ")[0]
|
||||
|
||||
// Select tag.
|
||||
if tag := selectTag(ver, tags); tag != "" {
|
||||
printf("selecting revision %q\n", tag)
|
||||
return run(dst, nil, v.cmd, v.checkout, v.updateRevFlag+tag)
|
||||
}
|
||||
|
||||
// No matching tag found, make default selection.
|
||||
printf("selecting tip\n")
|
||||
return run(dst, nil, v.cmd, v.update)
|
||||
}
|
||||
|
||||
// selectTag returns the closest matching tag for a given version.
|
||||
// Closest means the latest one that is not after the current release.
|
||||
// Version "release.rN" matches tags of the form "go.rN" (N being a decimal).
|
||||
// Version "weekly.YYYY-MM-DD" matches tags like "go.weekly.YYYY-MM-DD".
|
||||
func selectTag(goVersion string, tags []string) (match string) {
|
||||
const rPrefix = "release.r"
|
||||
if strings.HasPrefix(goVersion, rPrefix) {
|
||||
p := "go.r"
|
||||
v, err := strconv.Atof64(goVersion[len(rPrefix):])
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
var matchf float64
|
||||
for _, t := range tags {
|
||||
if !strings.HasPrefix(t, p) {
|
||||
continue
|
||||
}
|
||||
tf, err := strconv.Atof64(t[len(p):])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if matchf < tf && tf <= v {
|
||||
match, matchf = t, tf
|
||||
}
|
||||
}
|
||||
}
|
||||
const wPrefix = "weekly."
|
||||
if strings.HasPrefix(goVersion, wPrefix) {
|
||||
p := "go.weekly."
|
||||
v := goVersion[len(wPrefix):]
|
||||
for _, t := range tags {
|
||||
if !strings.HasPrefix(t, p) {
|
||||
continue
|
||||
}
|
||||
if match < t && t[len(p):] <= v {
|
||||
match = t
|
||||
}
|
||||
}
|
||||
}
|
||||
return match
|
||||
}
|
||||
|
||||
// checkoutRepo checks out repo into dst using vcs.
|
||||
|
||||
@@ -71,6 +71,13 @@ func errorf(format string, args ...interface{}) {
|
||||
logf(format, args...)
|
||||
}
|
||||
|
||||
func terrorf(tree *build.Tree, format string, args ...interface{}) {
|
||||
if tree != nil && tree.Goroot && os.Getenv("GOPATH") == "" {
|
||||
format = strings.TrimRight(format, "\n") + " ($GOPATH not set)\n"
|
||||
}
|
||||
errorf(format, args...)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
@@ -156,7 +163,7 @@ func logPackage(pkg string, tree *build.Tree) (logged bool) {
|
||||
name := filepath.Join(tree.Path, logfile)
|
||||
fout, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
logf("%s\n", err)
|
||||
terrorf(tree, "package log: %s\n", err)
|
||||
return false
|
||||
}
|
||||
fmt.Fprintf(fout, "%s\n", pkg)
|
||||
@@ -182,6 +189,12 @@ func install(pkg, parent string) {
|
||||
visit[pkg] = done
|
||||
}()
|
||||
|
||||
// Don't allow trailing '/'
|
||||
if _, f := filepath.Split(pkg); f == "" {
|
||||
errorf("%s should not have trailing '/'\n", pkg)
|
||||
return
|
||||
}
|
||||
|
||||
// Check whether package is local or remote.
|
||||
// If remote, download or update it.
|
||||
tree, pkg, err := build.FindTree(pkg)
|
||||
@@ -209,7 +222,7 @@ func install(pkg, parent string) {
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
errorf("%s: %v\n", pkg, err)
|
||||
terrorf(tree, "%s: %v\n", pkg, err)
|
||||
return
|
||||
}
|
||||
dir := filepath.Join(tree.SrcDir(), pkg)
|
||||
@@ -217,11 +230,11 @@ func install(pkg, parent string) {
|
||||
// Install prerequisites.
|
||||
dirInfo, err := build.ScanDir(dir, parent == "")
|
||||
if err != nil {
|
||||
errorf("%s: %v\n", pkg, err)
|
||||
terrorf(tree, "%s: %v\n", pkg, err)
|
||||
return
|
||||
}
|
||||
if len(dirInfo.GoFiles)+len(dirInfo.CgoFiles) == 0 {
|
||||
errorf("%s: package has no files\n", pkg)
|
||||
terrorf(tree, "%s: package has no files\n", pkg)
|
||||
return
|
||||
}
|
||||
for _, p := range dirInfo.Imports {
|
||||
@@ -237,13 +250,13 @@ func install(pkg, parent string) {
|
||||
if *useMake {
|
||||
err := domake(dir, pkg, tree, dirInfo.IsCommand())
|
||||
if err != nil {
|
||||
errorf("%s: install: %v\n", pkg, err)
|
||||
terrorf(tree, "%s: install: %v\n", pkg, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
script, err := build.Build(tree, pkg, dirInfo)
|
||||
if err != nil {
|
||||
errorf("%s: install: %v\n", pkg, err)
|
||||
terrorf(tree, "%s: install: %v\n", pkg, err)
|
||||
return
|
||||
}
|
||||
if *nuke {
|
||||
@@ -257,7 +270,7 @@ func install(pkg, parent string) {
|
||||
if script.Stale() {
|
||||
printf("%s: install\n", pkg)
|
||||
if err := script.Run(); err != nil {
|
||||
errorf("%s: install: %v\n", pkg, err)
|
||||
terrorf(tree, "%s: install: %v\n", pkg, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
||||
73
src/cmd/goinstall/tag_test.go
Normal file
73
src/cmd/goinstall/tag_test.go
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright 2011 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 main
|
||||
|
||||
import "testing"
|
||||
|
||||
var selectTagTestTags = []string{
|
||||
"go.r58",
|
||||
"go.r58.1",
|
||||
"go.r59",
|
||||
"go.r59.1",
|
||||
"go.r61",
|
||||
"go.r61.1",
|
||||
"go.weekly.2010-01-02",
|
||||
"go.weekly.2011-10-12",
|
||||
"go.weekly.2011-10-12.1",
|
||||
"go.weekly.2011-10-14",
|
||||
"go.weekly.2011-11-01",
|
||||
// these should be ignored:
|
||||
"release.r59",
|
||||
"release.r59.1",
|
||||
"release",
|
||||
"weekly.2011-10-12",
|
||||
"weekly.2011-10-12.1",
|
||||
"weekly",
|
||||
"foo",
|
||||
"bar",
|
||||
"go.f00",
|
||||
"go!r60",
|
||||
"go.1999-01-01",
|
||||
}
|
||||
|
||||
var selectTagTests = []struct {
|
||||
version string
|
||||
selected string
|
||||
}{
|
||||
{"release.r57", ""},
|
||||
{"release.r58.2", "go.r58.1"},
|
||||
{"release.r59", "go.r59"},
|
||||
{"release.r59.1", "go.r59.1"},
|
||||
{"release.r60", "go.r59.1"},
|
||||
{"release.r60.1", "go.r59.1"},
|
||||
{"release.r61", "go.r61"},
|
||||
{"release.r66", "go.r61.1"},
|
||||
{"weekly.2010-01-01", ""},
|
||||
{"weekly.2010-01-02", "go.weekly.2010-01-02"},
|
||||
{"weekly.2010-01-02.1", "go.weekly.2010-01-02"},
|
||||
{"weekly.2010-01-03", "go.weekly.2010-01-02"},
|
||||
{"weekly.2011-10-12", "go.weekly.2011-10-12"},
|
||||
{"weekly.2011-10-12.1", "go.weekly.2011-10-12.1"},
|
||||
{"weekly.2011-10-13", "go.weekly.2011-10-12.1"},
|
||||
{"weekly.2011-10-14", "go.weekly.2011-10-14"},
|
||||
{"weekly.2011-10-14.1", "go.weekly.2011-10-14"},
|
||||
{"weekly.2011-11-01", "go.weekly.2011-11-01"},
|
||||
{"weekly.2014-01-01", "go.weekly.2011-11-01"},
|
||||
{"weekly.3000-01-01", "go.weekly.2011-11-01"},
|
||||
// faulty versions:
|
||||
{"release.f00", ""},
|
||||
{"weekly.1999-01-01", ""},
|
||||
{"junk", ""},
|
||||
{"", ""},
|
||||
}
|
||||
|
||||
func TestSelectTag(t *testing.T) {
|
||||
for _, c := range selectTagTests {
|
||||
selected := selectTag(c.version, selectTagTestTags)
|
||||
if selected != c.selected {
|
||||
t.Errorf("selectTag(%q) = %q, want %q", c.version, selected, c.selected)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,7 +240,6 @@ NOTEST+=\
|
||||
../cmd/cgo\
|
||||
../cmd/ebnflint\
|
||||
../cmd/godoc\
|
||||
../cmd/goinstall\
|
||||
../cmd/gotest\
|
||||
../cmd/goyacc\
|
||||
../cmd/hgpatch\
|
||||
|
||||
@@ -294,7 +294,7 @@ This defines two templates, T1 and T2, and a third T3 that invokes the other two
|
||||
when it is executed.
|
||||
|
||||
The second way to build a template set is to use Set's Add method to add a
|
||||
parsed template to a set. A template may be bound at most one set. If it's
|
||||
parsed template to a set. A template may be bound to at most one set. If it's
|
||||
necessary to have a template in multiple sets, the template definition must be
|
||||
parsed multiple times to create distinct *Template values.
|
||||
|
||||
|
||||
@@ -196,23 +196,25 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
|
||||
val, _ := indirect(s.evalPipeline(dot, r.Pipe))
|
||||
// mark top of stack before any variables in the body are pushed.
|
||||
mark := s.mark()
|
||||
oneIteration := func(index, elem reflect.Value) {
|
||||
// Set top var (lexically the second if there are two) to the element.
|
||||
if len(r.Pipe.Decl) > 0 {
|
||||
s.setVar(1, elem)
|
||||
}
|
||||
// Set next var (lexically the first if there are two) to the index.
|
||||
if len(r.Pipe.Decl) > 1 {
|
||||
s.setVar(2, index)
|
||||
}
|
||||
s.walk(elem, r.List)
|
||||
s.pop(mark)
|
||||
}
|
||||
switch val.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
if val.Len() == 0 {
|
||||
break
|
||||
}
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
elem := val.Index(i)
|
||||
// Set top var (lexically the second if there are two) to the element.
|
||||
if len(r.Pipe.Decl) > 0 {
|
||||
s.setVar(1, elem)
|
||||
}
|
||||
// Set next var (lexically the first if there are two) to the index.
|
||||
if len(r.Pipe.Decl) > 1 {
|
||||
s.setVar(2, reflect.ValueOf(i))
|
||||
}
|
||||
s.walk(elem, r.List)
|
||||
s.pop(mark)
|
||||
oneIteration(reflect.ValueOf(i), val.Index(i))
|
||||
}
|
||||
return
|
||||
case reflect.Map:
|
||||
@@ -220,17 +222,23 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
|
||||
break
|
||||
}
|
||||
for _, key := range val.MapKeys() {
|
||||
elem := val.MapIndex(key)
|
||||
// Set top var (lexically the second if there are two) to the element.
|
||||
if len(r.Pipe.Decl) > 0 {
|
||||
s.setVar(1, elem)
|
||||
oneIteration(key, val.MapIndex(key))
|
||||
}
|
||||
return
|
||||
case reflect.Chan:
|
||||
if val.IsNil() {
|
||||
break
|
||||
}
|
||||
i := 0
|
||||
for ; ; i++ {
|
||||
elem, ok := val.Recv()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
// Set next var (lexically the first if there are two) to the key.
|
||||
if len(r.Pipe.Decl) > 1 {
|
||||
s.setVar(2, key)
|
||||
}
|
||||
s.walk(elem, r.List)
|
||||
s.pop(mark)
|
||||
oneIteration(reflect.ValueOf(i), elem)
|
||||
}
|
||||
if i == 0 {
|
||||
break
|
||||
}
|
||||
return
|
||||
case reflect.Invalid:
|
||||
@@ -498,7 +506,18 @@ func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Valu
|
||||
s.errorf("invalid value; expected %s", typ)
|
||||
}
|
||||
if !value.Type().AssignableTo(typ) {
|
||||
s.errorf("wrong type for value; expected %s; got %s", typ, value.Type())
|
||||
// Does one dereference or indirection work? We could do more, as we
|
||||
// do with method receivers, but that gets messy and method receivers
|
||||
// are much more constrained, so it makes more sense there than here.
|
||||
// Besides, one is almost always all you need.
|
||||
switch {
|
||||
case value.Kind() == reflect.Ptr && value.Elem().Type().AssignableTo(typ):
|
||||
value = value.Elem()
|
||||
case reflect.PtrTo(value.Type()).AssignableTo(typ) && value.CanAddr():
|
||||
value = value.Addr()
|
||||
default:
|
||||
s.errorf("wrong type for value; expected %s; got %s", typ, value.Type())
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
@@ -391,6 +391,8 @@ var execTests = []execTest{
|
||||
{"range $x $y MSIone", "{{range $x, $y := .MSIone}}<{{$x}}={{$y}}>{{end}}", "<one=1>", tVal, true},
|
||||
{"range $x PSI", "{{range $x := .PSI}}<{{$x}}>{{end}}", "<21><22><23>", tVal, true},
|
||||
{"declare in range", "{{range $x := .PSI}}<{{$foo:=$x}}{{$x}}>{{end}}", "<21><22><23>", tVal, true},
|
||||
{"range count", `{{range $i, $x := count 5}}[{{$i}}]{{$x}}{{end}}`, "[0]a[1]b[2]c[3]d[4]e", tVal, true},
|
||||
{"range nil count", `{{range $i, $x := count 0}}{{else}}empty{{end}}`, "empty", tVal, true},
|
||||
|
||||
// Cute examples.
|
||||
{"or as if true", `{{or .SI "slice is empty"}}`, "[3 4 5]", tVal, true},
|
||||
@@ -414,6 +416,11 @@ var execTests = []execTest{
|
||||
{"bug4", "{{if .Empty0}}non-nil{{else}}nil{{end}}", "nil", tVal, true},
|
||||
// Stringer.
|
||||
{"bug5", "{{.Str}}", "foozle", tVal, true},
|
||||
// Args need to be indirected and dereferenced sometimes.
|
||||
{"bug6a", "{{vfunc .V0 .V1}}", "vfunc", tVal, true},
|
||||
{"bug6b", "{{vfunc .V0 .V0}}", "vfunc", tVal, true},
|
||||
{"bug6c", "{{vfunc .V1 .V0}}", "vfunc", tVal, true},
|
||||
{"bug6d", "{{vfunc .V1 .V1}}", "vfunc", tVal, true},
|
||||
}
|
||||
|
||||
func zeroArgs() string {
|
||||
@@ -424,9 +431,35 @@ func oneArg(a string) string {
|
||||
return "oneArg=" + a
|
||||
}
|
||||
|
||||
// count returns a channel that will deliver n sequential 1-letter strings starting at "a"
|
||||
func count(n int) chan string {
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
c := make(chan string)
|
||||
go func() {
|
||||
for i := 0; i < n; i++ {
|
||||
c <- "abcdefghijklmnop"[i : i+1]
|
||||
}
|
||||
close(c)
|
||||
}()
|
||||
return c
|
||||
}
|
||||
|
||||
// vfunc takes a *V and a V
|
||||
func vfunc(V, *V) string {
|
||||
return "vfunc"
|
||||
}
|
||||
|
||||
func testExecute(execTests []execTest, set *Set, t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
funcs := FuncMap{"zeroArgs": zeroArgs, "oneArg": oneArg, "typeOf": typeOf}
|
||||
funcs := FuncMap{
|
||||
"count": count,
|
||||
"oneArg": oneArg,
|
||||
"typeOf": typeOf,
|
||||
"vfunc": vfunc,
|
||||
"zeroArgs": zeroArgs,
|
||||
}
|
||||
for _, test := range execTests {
|
||||
tmpl := New(test.name).Funcs(funcs)
|
||||
_, err := tmpl.ParseInSet(test.input, set)
|
||||
|
||||
@@ -278,53 +278,51 @@ func lexInsideAction(l *lexer) stateFn {
|
||||
// Either number, quoted string, or identifier.
|
||||
// Spaces separate and are ignored.
|
||||
// Pipe symbols separate and are emitted.
|
||||
for {
|
||||
if strings.HasPrefix(l.input[l.pos:], rightDelim) {
|
||||
return lexRightDelim
|
||||
}
|
||||
switch r := l.next(); {
|
||||
case r == eof || r == '\n':
|
||||
return l.errorf("unclosed action")
|
||||
case isSpace(r):
|
||||
l.ignore()
|
||||
case r == ':':
|
||||
if l.next() != '=' {
|
||||
return l.errorf("expected :=")
|
||||
}
|
||||
l.emit(itemColonEquals)
|
||||
case r == '|':
|
||||
l.emit(itemPipe)
|
||||
case r == '"':
|
||||
return lexQuote
|
||||
case r == '`':
|
||||
return lexRawQuote
|
||||
case r == '$':
|
||||
return lexIdentifier
|
||||
case r == '\'':
|
||||
return lexChar
|
||||
case r == '.':
|
||||
// special look-ahead for ".field" so we don't break l.backup().
|
||||
if l.pos < len(l.input) {
|
||||
r := l.input[l.pos]
|
||||
if r < '0' || '9' < r {
|
||||
return lexIdentifier // itemDot comes from the keyword table.
|
||||
}
|
||||
}
|
||||
fallthrough // '.' can start a number.
|
||||
case r == '+' || r == '-' || ('0' <= r && r <= '9'):
|
||||
l.backup()
|
||||
return lexNumber
|
||||
case isAlphaNumeric(r):
|
||||
l.backup()
|
||||
return lexIdentifier
|
||||
case r <= unicode.MaxASCII && unicode.IsPrint(r):
|
||||
l.emit(itemChar)
|
||||
return lexInsideAction
|
||||
default:
|
||||
return l.errorf("unrecognized character in action: %#U", r)
|
||||
}
|
||||
if strings.HasPrefix(l.input[l.pos:], rightDelim) {
|
||||
return lexRightDelim
|
||||
}
|
||||
return nil
|
||||
switch r := l.next(); {
|
||||
case r == eof || r == '\n':
|
||||
return l.errorf("unclosed action")
|
||||
case isSpace(r):
|
||||
l.ignore()
|
||||
case r == ':':
|
||||
if l.next() != '=' {
|
||||
return l.errorf("expected :=")
|
||||
}
|
||||
l.emit(itemColonEquals)
|
||||
case r == '|':
|
||||
l.emit(itemPipe)
|
||||
case r == '"':
|
||||
return lexQuote
|
||||
case r == '`':
|
||||
return lexRawQuote
|
||||
case r == '$':
|
||||
return lexIdentifier
|
||||
case r == '\'':
|
||||
return lexChar
|
||||
case r == '.':
|
||||
// special look-ahead for ".field" so we don't break l.backup().
|
||||
if l.pos < len(l.input) {
|
||||
r := l.input[l.pos]
|
||||
if r < '0' || '9' < r {
|
||||
return lexIdentifier // itemDot comes from the keyword table.
|
||||
}
|
||||
}
|
||||
fallthrough // '.' can start a number.
|
||||
case r == '+' || r == '-' || ('0' <= r && r <= '9'):
|
||||
l.backup()
|
||||
return lexNumber
|
||||
case isAlphaNumeric(r):
|
||||
l.backup()
|
||||
return lexIdentifier
|
||||
case r <= unicode.MaxASCII && unicode.IsPrint(r):
|
||||
l.emit(itemChar)
|
||||
return lexInsideAction
|
||||
default:
|
||||
return l.errorf("unrecognized character in action: %#U", r)
|
||||
}
|
||||
return lexInsideAction
|
||||
}
|
||||
|
||||
// lexIdentifier scans an alphanumeric or field.
|
||||
|
||||
@@ -184,6 +184,20 @@ var lexTests = []lexTest{
|
||||
tLeft,
|
||||
{itemError, `bad number syntax: "3k"`},
|
||||
}},
|
||||
|
||||
// Fixed bugs
|
||||
// Many elements in an action blew the lookahead until
|
||||
// we made lexInsideAction not loop.
|
||||
{"long pipeline deadlock", "{{|||||}}", []item{
|
||||
tLeft,
|
||||
tPipe,
|
||||
tPipe,
|
||||
tPipe,
|
||||
tPipe,
|
||||
tPipe,
|
||||
tRight,
|
||||
tEOF,
|
||||
}},
|
||||
}
|
||||
|
||||
// collect gathers the emitted items into a slice.
|
||||
|
||||
@@ -35,8 +35,8 @@ const (
|
||||
NodeBool // A boolean constant.
|
||||
NodeCommand // An element of a pipeline.
|
||||
NodeDot // The cursor, dot.
|
||||
NodeElse // An else action.
|
||||
NodeEnd // An end action.
|
||||
nodeElse // An else action. Not added to tree.
|
||||
nodeEnd // An end action. Not added to tree.
|
||||
NodeField // A field or method name.
|
||||
NodeIdentifier // An identifier; always a function name.
|
||||
NodeIf // An if action.
|
||||
@@ -356,36 +356,37 @@ func (s *StringNode) String() string {
|
||||
return fmt.Sprintf("S=%#q", s.Text)
|
||||
}
|
||||
|
||||
// EndNode represents an {{end}} action. It is represented by a nil pointer.
|
||||
type EndNode bool
|
||||
// endNode represents an {{end}} action. It is represented by a nil pointer.
|
||||
// It does not appear in the final parse tree.
|
||||
type endNode bool
|
||||
|
||||
func newEnd() *EndNode {
|
||||
func newEnd() *endNode {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EndNode) Type() NodeType {
|
||||
return NodeEnd
|
||||
func (e *endNode) Type() NodeType {
|
||||
return nodeEnd
|
||||
}
|
||||
|
||||
func (e *EndNode) String() string {
|
||||
func (e *endNode) String() string {
|
||||
return "{{end}}"
|
||||
}
|
||||
|
||||
// ElseNode represents an {{else}} action.
|
||||
type ElseNode struct {
|
||||
// elseNode represents an {{else}} action. Does not appear in the final tree.
|
||||
type elseNode struct {
|
||||
NodeType
|
||||
Line int // The line number in the input.
|
||||
}
|
||||
|
||||
func newElse(line int) *ElseNode {
|
||||
return &ElseNode{NodeType: NodeElse, Line: line}
|
||||
func newElse(line int) *elseNode {
|
||||
return &elseNode{NodeType: nodeElse, Line: line}
|
||||
}
|
||||
|
||||
func (e *ElseNode) Type() NodeType {
|
||||
return NodeElse
|
||||
func (e *elseNode) Type() NodeType {
|
||||
return nodeElse
|
||||
}
|
||||
|
||||
func (e *ElseNode) String() string {
|
||||
func (e *elseNode) String() string {
|
||||
return "{{else}}"
|
||||
}
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ func (t *Tree) itemList(toEOF bool) (list *ListNode, next Node) {
|
||||
for t.peek().typ != itemEOF {
|
||||
n := t.textOrAction()
|
||||
switch n.Type() {
|
||||
case NodeEnd, NodeElse:
|
||||
case nodeEnd, nodeElse:
|
||||
return list, n
|
||||
}
|
||||
list.append(n)
|
||||
@@ -278,10 +278,10 @@ func (t *Tree) parseControl(context string) (lineNum int, pipe *PipeNode, list,
|
||||
var next Node
|
||||
list, next = t.itemList(false)
|
||||
switch next.Type() {
|
||||
case NodeEnd: //done
|
||||
case NodeElse:
|
||||
case nodeEnd: //done
|
||||
case nodeElse:
|
||||
elseList, next = t.itemList(false)
|
||||
if next.Type() != NodeEnd {
|
||||
if next.Type() != nodeEnd {
|
||||
t.errorf("expected end; found %s", next)
|
||||
}
|
||||
elseList = elseList
|
||||
|
||||
@@ -37,7 +37,7 @@ func Set(text string, funcs ...map[string]interface{}) (tree map[string]*Tree, e
|
||||
if end == nil {
|
||||
t.errorf("unexpected EOF in %s", context)
|
||||
}
|
||||
if end.Type() != NodeEnd {
|
||||
if end.Type() != nodeEnd {
|
||||
t.errorf("unexpected %s in %s", end, context)
|
||||
}
|
||||
t.stopParse()
|
||||
|
||||
Reference in New Issue
Block a user