Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/llgo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -393,3 +393,19 @@ jobs:
GOOS=js GOARCH=wasm llgo build -o "$RUNNER_TEMP/runtime-js.wasm" ./internal/build/testdata/wasm-runtime
GOOS=wasip1 GOARCH=wasm llgo build -o "$RUNNER_TEMP/runtime-wasip1.wasm" ./internal/build/testdata/wasm-runtime
file "$RUNNER_TEMP/runtime-js.wasm" "$RUNNER_TEMP/runtime-wasip1.wasm"

- name: Validate embedded and external Wasm DWARF
if: matrix.go == '1.26.5'
shell: bash
run: |
GOOS=wasip1 GOARCH=wasm llgo build -debug-artifact=embedded -o "$RUNNER_TEMP/runtime-debug.wasm" ./internal/build/testdata/wasm-runtime
llvm-objdump -h "$RUNNER_TEMP/runtime-debug.wasm" > "$RUNNER_TEMP/runtime-debug.sections"
grep -q '\.debug_info' "$RUNNER_TEMP/runtime-debug.sections"

GOOS=wasip1 GOARCH=wasm llgo build -debug-artifact=external -o "$RUNNER_TEMP/runtime-external.wasm" ./internal/build/testdata/wasm-runtime
test -s "$RUNNER_TEMP/runtime-external.debug.wasm"
llvm-objdump -h "$RUNNER_TEMP/runtime-external.wasm" > "$RUNNER_TEMP/runtime-external.sections"
llvm-objdump -h "$RUNNER_TEMP/runtime-external.debug.wasm" > "$RUNNER_TEMP/runtime-external-debug.sections"
grep -q 'external_debug_info' "$RUNNER_TEMP/runtime-external.sections"
! grep -q '\.debug_info' "$RUNNER_TEMP/runtime-external.sections"
grep -q '\.debug_info' "$RUNNER_TEMP/runtime-external-debug.sections"
59 changes: 59 additions & 0 deletions cmd/internal/flags/debug_artifact.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package flags

import (
"flag"
"fmt"

"github.com/goplus/llgo/internal/build"
)

type debugArtifactFlag struct {
Specified bool
Mode build.DebugArtifactMode
}

func (f *debugArtifactFlag) String() string {
return f.Mode.String()
}

func (f *debugArtifactFlag) Set(value string) error {
var mode build.DebugArtifactMode
switch value {
case "embedded":
mode = build.DebugArtifactEmbedded
case "external":
mode = build.DebugArtifactExternal
case "host":
mode = build.DebugArtifactHost
case "none":
mode = build.DebugArtifactNone
default:
return fmt.Errorf("invalid debug artifact mode %q (valid: embedded, external, host, none)", value)
}
f.Specified = true
f.Mode = mode
return nil
}

var DebugArtifact debugArtifactFlag

func addDebugArtifactFlag(fs *flag.FlagSet) {
DebugArtifact = debugArtifactFlag{Mode: build.DebugArtifactDefault}
fs.Var(&DebugArtifact, "debug-artifact", "DWARF artifact mode: embedded, external, host, or none")
}
5 changes: 5 additions & 0 deletions cmd/internal/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ func AddBuildFlags(fs *flag.FlagSet) {
AddLTOFlag(fs)
AddGlobalDCEFlag(fs)
addPCLNFlag(fs)
addDebugArtifactFlag(fs)
fs.StringVar(&Tags, "tags", "", "Build tags")
fs.StringVar(&BuildEnv, "buildenv", "", "Build environment")
fs.Var(&PthreadStackSize, "pthread-stack-size", "Stack size for pthread-backed goroutines, e.g. 32MB or 1024KB (0 uses the platform default)")
Expand Down Expand Up @@ -372,6 +373,10 @@ func UpdateConfig(conf *build.Config) error {
conf.PCLNMode = PCLN.Mode
conf.PCLNModeSet = true
}
if DebugArtifact.Specified {
conf.DebugArtifactMode = DebugArtifact.Mode
conf.DebugArtifactModeSet = true
}
if LTOPluginPath != "" {
if conf.LTO != lto.Full {
return fmt.Errorf("lto pass plugin can only be enabled with full LTO (-lto=full)")
Expand Down
46 changes: 46 additions & 0 deletions cmd/internal/flags/flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,52 @@ func TestBuildPCLNFlagInvalid(t *testing.T) {
}
}

func TestBuildDebugArtifactFlags(t *testing.T) {
tests := []struct {
name string
args []string
want build.DebugArtifactMode
specified bool
}{
{name: "default", want: build.DebugArtifactDefault},
{name: "embedded", args: []string{"-debug-artifact=embedded"}, want: build.DebugArtifactEmbedded, specified: true},
{name: "external", args: []string{"-debug-artifact=external"}, want: build.DebugArtifactExternal, specified: true},
{name: "host", args: []string{"-debug-artifact=host"}, want: build.DebugArtifactHost, specified: true},
{name: "none", args: []string{"-debug-artifact=none"}, want: build.DebugArtifactNone, specified: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fs := flag.NewFlagSet(tt.name, flag.ContinueOnError)
fs.SetOutput(new(bytes.Buffer))
AddBuildFlags(fs)
if err := fs.Parse(tt.args); err != nil {
t.Fatal(err)
}
if DebugArtifact.Specified != tt.specified || DebugArtifact.Mode != tt.want {
t.Fatalf("DebugArtifact = %+v, want mode=%v specified=%v", DebugArtifact, tt.want, tt.specified)
}
conf := &build.Config{}
if err := UpdateConfig(conf); err != nil {
t.Fatal(err)
}
if conf.DebugArtifactMode != tt.want || conf.DebugArtifactModeSet != tt.specified {
t.Fatalf("Config debug artifact = %v/%v, want %v/%v", conf.DebugArtifactMode, conf.DebugArtifactModeSet, tt.want, tt.specified)
}
})
}
}

func TestBuildDebugArtifactFlagInvalid(t *testing.T) {
for _, args := range [][]string{{"-debug-artifact"}, {"-debug-artifact="}, {"-debug-artifact=default"}, {"-debug-artifact=EXTERNAL"}} {
fs := flag.NewFlagSet("invalid-debug-artifact", flag.ContinueOnError)
fs.SetOutput(new(bytes.Buffer))
AddBuildFlags(fs)
if err := fs.Parse(args); err == nil {
t.Fatalf("Parse(%v) succeeded", args)
}
}
}

func TestUpdateConfigPreservesPCLNModeWhenUnspecified(t *testing.T) {
fs := flag.NewFlagSet("pclntab-unspecified", flag.ContinueOnError)
fs.SetOutput(new(bytes.Buffer))
Expand Down
33 changes: 21 additions & 12 deletions internal/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,14 @@ type OutFmts struct {

// OutFmtDetails contains detailed output file paths for each format
type OutFmtDetails struct {
Out string // Base output file path
PCLN string // PCLN sidecar output file path (.pclntab)
Bin string // Binary output file path (.bin)
Hex string // Intel hex output file path (.hex)
Img string // Image output file path (.img)
Uf2 string // UF2 output file path (.uf2)
Zip string // ZIP/DFU output file path (.zip)
Out string // Base output file path
PCLN string // PCLN sidecar output file path (.pclntab)
DWARF string // External debugger-owned DWARF container (.debug.wasm)
Bin string // Binary output file path (.bin)
Hex string // Intel hex output file path (.hex)
Img string // Image output file path (.img)
Uf2 string // UF2 output file path (.uf2)
Zip string // ZIP/DFU output file path (.zip)
}

// ModuleHook observes a package module immediately after it is generated and
Expand Down Expand Up @@ -174,8 +175,12 @@ type Config struct {
GoBuildFlags []string
// BuildParallelism is the package-level concurrency requested by Go's -p
// build flag for llgo test. Zero uses the Go default, GOMAXPROCS.
BuildParallelism int
LinkOptions LinkOptions
BuildParallelism int
LinkOptions LinkOptions
DebugArtifactMode DebugArtifactMode
// DebugArtifactModeSet distinguishes an explicit command request from the
// effective mode derived from -w and the current build default.
DebugArtifactModeSet bool
// OmitDWARFByDefault controls linked builds only when -w was not
// explicitly specified. Explicit -w and -w=false always win.
OmitDWARFByDefault bool
Expand Down Expand Up @@ -392,6 +397,9 @@ func Build(inv Invocation) ([]Package, error) {
if conf.Target != "" && export.GOARCH != "" {
conf.Goarch = export.GOARCH
}
if err := resolveDebugArtifactMode(conf, &export); err != nil {
return nil, err
}
if err := validateLinkOptions(conf, &export); err != nil {
return nil, err
}
Expand Down Expand Up @@ -663,6 +671,9 @@ func Build(inv Invocation) ([]Package, error) {
if err := finalizeRuntimePCLN(ctx, outFmts, verbose); err != nil {
return nil, err
}
if err := finalizeDebugArtifact(conf, outFmts, verbose); err != nil {
return nil, err
}
if conf.Mode == ModeBuild && conf.SizeReport {
if err := reportBinarySize(outFmts.Out, conf.SizeFormat, conf.SizeLevel, allPkgs); err != nil {
fmt.Fprintf(os.Stderr, "Warning: size report failed: %v\n", err)
Expand Down Expand Up @@ -1534,9 +1545,7 @@ func linkObjFiles(ctx *context, app string, objFiles, linkArgs []string, verbose
buildArgs = append(buildArgs, linuxExportDynamicArgs(ctx)...)
}

if shouldEmitDebugInfo(ctx.buildConf, &ctx.crossCompile) {
buildArgs = append(buildArgs, "-gdwarf-4")
}
buildArgs = append(buildArgs, dwarfPreserveLinkerArgs(ctx.buildConf, &ctx.crossCompile)...)

if ctx.buildConf.GenLL {
var compiledObjFiles []string
Expand Down
1 change: 1 addition & 0 deletions internal/build/clean.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func cleanMainPkg(pkg *packages.Package, conf *Config, verbose bool) {
func removeExecutableArtifacts(executable string, verbose bool) {
removeFile(executable, verbose)
removeFile(pclnSidecarPath(executable), verbose)
removeFile(dwarfSidecarPath(executable), verbose)
}

func cleanPkgs(initial []*packages.Package, verbose bool) {
Expand Down
6 changes: 3 additions & 3 deletions internal/build/clean_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/goplus/llgo/internal/packages"
)

func TestCleanMainPkgRemovesPCLNSidecars(t *testing.T) {
func TestCleanMainPkgRemovesSidecars(t *testing.T) {
root := t.TempDir()
binDir := filepath.Join(root, "bin")
sourceDir := filepath.Join(root, "source")
Expand All @@ -30,7 +30,7 @@ func TestCleanMainPkgRemovesPCLNSidecars(t *testing.T) {
filepath.Join(sourceDir, "demo.exe"),
}
for _, executable := range outputs {
for _, artifact := range []string{executable, pclnSidecarPath(executable)} {
for _, artifact := range []string{executable, pclnSidecarPath(executable), dwarfSidecarPath(executable)} {
if err := os.WriteFile(artifact, []byte("artifact"), 0o644); err != nil {
t.Fatal(err)
}
Expand All @@ -44,7 +44,7 @@ func TestCleanMainPkgRemovesPCLNSidecars(t *testing.T) {
cleanMainPkg(pkg, conf, false)

for _, executable := range outputs {
for _, artifact := range []string{executable, pclnSidecarPath(executable)} {
for _, artifact := range []string{executable, pclnSidecarPath(executable), dwarfSidecarPath(executable)} {
if _, err := os.Stat(artifact); !os.IsNotExist(err) {
t.Errorf("artifact %q still exists (stat error %v)", artifact, err)
}
Expand Down
4 changes: 2 additions & 2 deletions internal/build/collect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ func TestCollectFingerprintIncludesEmitDWARF(t *testing.T) {
}

targetWithoutDWARF := newPkg()
if err := newContext(LinkOptions{}, crosscompile.DebugInfoPolicy{AlwaysOmit: true}).collectFingerprint(targetWithoutDWARF); err != nil {
if err := newContext(LinkOptions{}, crosscompile.DebugInfoPolicy{Capability: crosscompile.DebugInfoUnavailable}).collectFingerprint(targetWithoutDWARF); err != nil {
t.Fatal(err)
}
if withDWARF.Fingerprint == targetWithoutDWARF.Fingerprint {
Expand All @@ -197,7 +197,7 @@ func TestCollectFingerprintIncludesEmitDWARF(t *testing.T) {
t.Fatal(err)
}
if targetData.Common != nil && targetData.Common.EmitDWARF {
t.Fatalf("always-omit target manifest unexpectedly contains EMIT_DWARF=true:\n%s", targetWithoutDWARF.Manifest)
t.Fatalf("target without DWARF support unexpectedly contains EMIT_DWARF=true:\n%s", targetWithoutDWARF.Manifest)
}
}

Expand Down
Loading
Loading