Skip to content

build: add lg_no_http to drop net/http from the runtime (#652) - #658

Merged
mparrett merged 1 commit into
mainfrom
wt/no-http-tag
Aug 5, 2026
Merged

build: add lg_no_http to drop net/http from the runtime (#652)#658
mparrett merged 1 commit into
mainfrom
wt/no-http-tag

Conversation

@mparrett

Copy link
Copy Markdown
Collaborator

Part of #652 — the http half.

Why

installHttpNS is registered from an init(). That makes net/http, and crypto/tls and crypto/x509 behind it, reachable in every binary that links pkg/rt, whether or not the program ever opens a socket. The linker cannot prove it unused, so it stays.

This is one concrete piece of the flat AOT floor described in #652, where a hello-world and a fib(35) binary differ by 16 bytes. Nothing gets eliminated because nothing is unreachable.

The tag

go build -tags lg_no_http leaves it out.

Measured on darwin/arm64 against cmd/lg-runtime:

build bytes delta
default 17,594,402
lg_no_http 12,175,106 −5,419,296 (−30.8%)
default, -s -w 12,131,874
lg_no_http, -s -w 8,382,754 −3,749,120 (−30.9%)

go tool nm finds 3,083 net/http, crypto/tls, and crypto/x509 symbols in the default binary and zero in the tagged one.

That is well above the ~1.9 MB I estimated on the issue from go tool nm -size totals. The symbol table undercounts what a package drags in: type metadata, pclntab, and rodata attributable to those trees are not in the per-symbol sizes I grouped.

Behavior under the tag

The http namespace is not installed, so http/get, http/serve, and the rest do not resolve. io/reader on an io/url record returns an error rather than fetching. Everything else is untouched: file and resource slurp, io/reader on a path, and the remainder of io behave identically. A bare-string (slurp "https://…") was already treated as a file path before this change, so that is unchanged too.

The stub file

TinyGo's net/http does not compile, so that lane has never had a working http namespace and gains nothing new from the tag. It already carried exactly the stub this needs, so http_tinygo.go becomes http_stub.go under tinygo || lg_no_http and serves both lanes.

Scope

The http half of #652 only. The other build-tag-reachable tree named there, go/parser and go/printer via pkg/rt/gogen.go at about 232 KB, is left alone since #628 and #657 are both in flight on gogen. It composes as a separate tag later if that shape is wanted.

I went with a narrow per-feature tag rather than one umbrella "runtime-only" tag. An umbrella forces all-or-nothing and its meaning drifts as things are added to it; lg_no_http composes with whatever comes next. Happy to fold it into a broader tag instead if you'd rather have one knob.

CI

Nothing in the default build compiles http_stub.go, so a signature change in http.go would break the tagged lane silently. no-http-build builds it, boots it, and asserts net/http stayed out, the last part so the size win cannot regress unnoticed.

Checks

gofmt and go vet clean on both tag lanes. go test ./pkg/... passes with the tag. The default suite is unchanged apart from TestGogenAOTDiff, which needs a generated tree absent from a fresh worktree and fails the same way on an untouched checkout. Cross-builds pass for linux/amd64, js/wasm, plan9/amd64, and wasip1/wasm, each with and without the tag. tinygo build -target=wasi still produces a booting module after the rename.

installHttpNS is registered from an init(), so net/http — and crypto/tls and
crypto/x509 behind it — is reachable in every binary linking pkg/rt, whether or
not the program ever opens a socket. The linker has no way to see it is unused,
which is part of why an AOT hello-world and an AOT fib(35) differ by 16 bytes.

Build with -tags lg_no_http to leave it out. On darwin/arm64, cmd/lg-runtime
goes 17,594,402 -> 12,175,106 bytes, and 12,131,874 -> 8,382,754 with -s -w:
about 30% either way, and `go tool nm` finds zero net/http or crypto symbols in
the tagged binary. That is more than the ~1.9 MB the issue estimated from symbol
sizes, because the symbol table undercounts what a package drags in.

TinyGo's net/http does not compile, so that lane has always built without the
namespace and gains nothing new here — but it already had exactly the stub this
tag needs, so http_tinygo.go becomes http_stub.go and serves both lanes off
`tinygo || lg_no_http`. One stub, two reasons.

This is the http half of #652 only. The other tagged tree named there,
go/parser and go/printer via pkg/rt/gogen.go, is left alone because #628 and
#657 are both in flight on gogen.

The no-http-build CI job exists because nothing in the default build compiles
http_stub.go; without it a signature change in http.go would break the tagged
lane silently. It also asserts net/http stayed out, so the size win cannot
regress unnoticed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mparrett
mparrett requested review from nnunley and nooga July 31, 2026 05:05

@nooga nooga left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Verified the headline claim directly — built cmd/lg-runtime both ways and checked with go tool nm: 2735 net/http/crypto/tls symbol hits by default, 0 under -tags lg_no_http. No leftover unconditional imports outside the two intentionally-tagged files. Manually exercised the stub's three documented behaviors (http/get unresolved, URL-reader error, bare-string slurp unaffected) and they match what the PR/docs claim.

Verified: go build/go test clean on both tag lanes; go vet/gofmt clean.

One gap worth a fast follow-up rather than blocking this: the new no-http-build CI job only checks the binary links/boots and that the symbols are absent — it never asserts the stub's actual error text or that the http namespace is genuinely unresolvable. That's currently verified by hand (mine, and presumably the author's). A small -tags lg_no_http Go test asserting those error strings would close the loop.

Minor nit: http_stub.go's error message ends with "(under tinygo, use JS fetch)" for both the tinygo and lg_no_http tags — confusing for a plain lg_no_http embedder who has nothing to do with tinygo.

@mparrett
mparrett merged commit c7d3da3 into main Aug 5, 2026
19 checks passed
@mparrett
mparrett deleted the wt/no-http-tag branch August 5, 2026 00:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants