diff --git a/cmd/common/compile.go b/cmd/common/compile.go index 1edec44d..764a9c51 100644 --- a/cmd/common/compile.go +++ b/cmd/common/compile.go @@ -18,7 +18,8 @@ const makefileName = "Makefile" var defaultWasmOutput = filepath.Join("wasm", "workflow.wasm") // getBuildCmd returns a single step that builds the workflow and returns the WASM bytes. -func getBuildCmd(workflowRootFolder, mainFile, language string) (func() ([]byte, error), error) { +// If stripSymbols is true, debug symbols are stripped from the binary to reduce size. +func getBuildCmd(workflowRootFolder, mainFile, language string, stripSymbols bool) (func() ([]byte, error), error) { tmpPath := filepath.Join(workflowRootFolder, ".cre_build_tmp.wasm") switch language { case constants.WorkflowLanguageTypeScript: @@ -33,6 +34,32 @@ func getBuildCmd(workflowRootFolder, mainFile, language string) (func() ([]byte, _ = os.Remove(tmpPath) return b, err }, nil + case constants.WorkflowLanguageGolang: + // Build the package (.) so all .go files (main.go, workflow.go, etc.) are compiled together + ldflags := "-buildid=" + if stripSymbols { + ldflags = "-buildid= -w -s" + } + cmd := exec.Command( + "go", "build", + "-o", tmpPath, + "-trimpath", + "-buildvcs=false", + "-mod=readonly", + "-ldflags="+ldflags, + ".", + ) + cmd.Dir = workflowRootFolder + cmd.Env = append(os.Environ(), "GOOS=wasip1", "GOARCH=wasm", "CGO_ENABLED=0") + return func() ([]byte, error) { + out, err := cmd.CombinedOutput() + if err != nil { + return nil, fmt.Errorf("%w\nbuild output:\n%s", err, strings.TrimSpace(string(out))) + } + b, err := os.ReadFile(tmpPath) + _ = os.Remove(tmpPath) + return b, err + }, nil case constants.WorkflowLanguageWasm: makeRoot, err := findMakefileRoot(workflowRootFolder) if err != nil { @@ -49,13 +76,18 @@ func getBuildCmd(workflowRootFolder, mainFile, language string) (func() ([]byte, return os.ReadFile(builtPath) }, nil default: + // Build the package (.) so all .go files are compiled together + ldflags := "-buildid=" + if stripSymbols { + ldflags = "-buildid= -w -s" + } cmd := exec.Command( "go", "build", "-o", tmpPath, "-trimpath", "-buildvcs=false", "-mod=readonly", - "-ldflags=-buildid= -w -s", + "-ldflags="+ldflags, ".", ) cmd.Dir = workflowRootFolder @@ -73,7 +105,10 @@ func getBuildCmd(workflowRootFolder, mainFile, language string) (func() ([]byte, } // CompileWorkflowToWasm compiles the workflow at workflowPath and returns the WASM binary. -func CompileWorkflowToWasm(workflowPath string) ([]byte, error) { +// If stripSymbols is true, debug symbols are stripped to reduce binary size (used for deploy). +// If false, debug symbols are kept for better error messages (used for simulate). +// For custom builds (WASM language with Makefile), stripSymbols has no effect. +func CompileWorkflowToWasm(workflowPath string, stripSymbols bool) ([]byte, error) { workflowRootFolder, workflowMainFile, err := WorkflowPathRootAndMain(workflowPath) if err != nil { return nil, fmt.Errorf("workflow path: %w", err) @@ -105,7 +140,7 @@ func CompileWorkflowToWasm(workflowPath string) ([]byte, error) { return nil, fmt.Errorf("unsupported workflow language for file %s", workflowMainFile) } - buildStep, err := getBuildCmd(workflowRootFolder, workflowMainFile, language) + buildStep, err := getBuildCmd(workflowRootFolder, workflowMainFile, language, stripSymbols) if err != nil { return nil, err } diff --git a/cmd/common/compile_test.go b/cmd/common/compile_test.go index 90001d9e..de8952a4 100644 --- a/cmd/common/compile_test.go +++ b/cmd/common/compile_test.go @@ -46,21 +46,21 @@ func TestFindMakefileRoot(t *testing.T) { func TestCompileWorkflowToWasm_Go_Success(t *testing.T) { t.Run("basic_workflow", func(t *testing.T) { path := deployTestdataPath("basic_workflow", "main.go") - wasm, err := CompileWorkflowToWasm(path) + wasm, err := CompileWorkflowToWasm(path, true) require.NoError(t, err) assert.NotEmpty(t, wasm) }) t.Run("configless_workflow", func(t *testing.T) { path := deployTestdataPath("configless_workflow", "main.go") - wasm, err := CompileWorkflowToWasm(path) + wasm, err := CompileWorkflowToWasm(path, true) require.NoError(t, err) assert.NotEmpty(t, wasm) }) t.Run("missing_go_mod", func(t *testing.T) { path := deployTestdataPath("missing_go_mod", "main.go") - wasm, err := CompileWorkflowToWasm(path) + wasm, err := CompileWorkflowToWasm(path, true) require.NoError(t, err) assert.NotEmpty(t, wasm) }) @@ -68,7 +68,7 @@ func TestCompileWorkflowToWasm_Go_Success(t *testing.T) { func TestCompileWorkflowToWasm_Go_Malformed_Fails(t *testing.T) { path := deployTestdataPath("malformed_workflow", "main.go") - _, err := CompileWorkflowToWasm(path) + _, err := CompileWorkflowToWasm(path, true) require.Error(t, err) assert.Contains(t, err.Error(), "failed to compile workflow") assert.Contains(t, err.Error(), "undefined: sdk.RemovedFunctionThatFailsCompilation") @@ -79,7 +79,7 @@ func TestCompileWorkflowToWasm_Wasm_Success(t *testing.T) { _ = os.Remove(wasmPath) t.Cleanup(func() { _ = os.Remove(wasmPath) }) - wasm, err := CompileWorkflowToWasm(wasmPath) + wasm, err := CompileWorkflowToWasm(wasmPath, true) require.NoError(t, err) assert.NotEmpty(t, wasm) @@ -95,14 +95,14 @@ func TestCompileWorkflowToWasm_Wasm_Fails(t *testing.T) { wasmPath := filepath.Join(wasmDir, "workflow.wasm") require.NoError(t, os.WriteFile(wasmPath, []byte("not really wasm"), 0600)) - _, err := CompileWorkflowToWasm(wasmPath) + _, err := CompileWorkflowToWasm(wasmPath, true) require.Error(t, err) assert.Contains(t, err.Error(), "no Makefile found") }) t.Run("make_build_fails", func(t *testing.T) { path := deployTestdataPath("wasm_make_fails", "wasm", "workflow.wasm") - _, err := CompileWorkflowToWasm(path) + _, err := CompileWorkflowToWasm(path, true) require.Error(t, err) assert.Contains(t, err.Error(), "failed to compile workflow") assert.Contains(t, err.Error(), "build output:") @@ -126,7 +126,7 @@ func TestCompileWorkflowToWasm_TS_Success(t *testing.T) { if err := install.Run(); err != nil { t.Skipf("bun install failed (network or cre-sdk): %v", err) } - wasm, err := CompileWorkflowToWasm(mainPath) + wasm, err := CompileWorkflowToWasm(mainPath, true) if err != nil { t.Skipf("TS compile failed (published cre-sdk may lack full layout): %v", err) } diff --git a/cmd/workflow/build/build.go b/cmd/workflow/build/build.go index aca1a7bb..9ff70621 100644 --- a/cmd/workflow/build/build.go +++ b/cmd/workflow/build/build.go @@ -58,7 +58,7 @@ func execute(workflowFolder, outputPath string) error { outputPath = cmdcommon.EnsureWasmExtension(outputPath) ui.Dim("Compiling workflow...") - wasmBytes, err := cmdcommon.CompileWorkflowToWasm(resolvedPath) + wasmBytes, err := cmdcommon.CompileWorkflowToWasm(resolvedPath, true) if err != nil { ui.Error("Build failed:") return fmt.Errorf("failed to compile workflow: %w", err) diff --git a/cmd/workflow/deploy/compile.go b/cmd/workflow/deploy/compile.go index f2b1281d..4fa27b49 100644 --- a/cmd/workflow/deploy/compile.go +++ b/cmd/workflow/deploy/compile.go @@ -67,7 +67,7 @@ func (h *handler) Compile() error { h.runtimeContext.Workflow.Language = cmdcommon.GetWorkflowLanguage(workflowMainFile) } - wasmFile, err = cmdcommon.CompileWorkflowToWasm(resolvedWorkflowPath) + wasmFile, err = cmdcommon.CompileWorkflowToWasm(resolvedWorkflowPath, true) if err != nil { ui.Error("Build failed:") return fmt.Errorf("failed to compile workflow: %w", err) diff --git a/cmd/workflow/deploy/compile_test.go b/cmd/workflow/deploy/compile_test.go index 02317b11..acc2a30d 100644 --- a/cmd/workflow/deploy/compile_test.go +++ b/cmd/workflow/deploy/compile_test.go @@ -295,7 +295,7 @@ func outputPathWithExtensions(path string) string { // file content equals CompileWorkflowToWasm(workflowPath) + brotli + base64. func assertCompileOutputMatchesUnderlying(t *testing.T, simulatedEnvironment *chainsim.SimulatedEnvironment, inputs Inputs, ownerType string) { t.Helper() - wasm, err := cmdcommon.CompileWorkflowToWasm(inputs.WorkflowPath) + wasm, err := cmdcommon.CompileWorkflowToWasm(inputs.WorkflowPath, true) require.NoError(t, err) compressed, err := cmdcommon.CompressBrotli(wasm) require.NoError(t, err) diff --git a/cmd/workflow/hash/hash.go b/cmd/workflow/hash/hash.go index a5cf1bee..b4ac33bd 100644 --- a/cmd/workflow/hash/hash.go +++ b/cmd/workflow/hash/hash.go @@ -155,7 +155,7 @@ func loadBinary(wasmFlag, workflowPathFromSettings string) ([]byte, error) { spinner := ui.NewSpinner() spinner.Start("Compiling workflow...") - wasmBytes, err := cmdcommon.CompileWorkflowToWasm(resolvedWorkflowPath) + wasmBytes, err := cmdcommon.CompileWorkflowToWasm(resolvedWorkflowPath, true) spinner.Stop() if err != nil { ui.Error("Build failed:") diff --git a/cmd/workflow/simulate/simulate.go b/cmd/workflow/simulate/simulate.go index c0d9077c..12a33b84 100644 --- a/cmd/workflow/simulate/simulate.go +++ b/cmd/workflow/simulate/simulate.go @@ -326,7 +326,7 @@ func (h *handler) Execute(inputs Inputs) error { spinner := ui.NewSpinner() spinner.Start("Compiling workflow...") - wasmFileBinary, err = cmdcommon.CompileWorkflowToWasm(resolvedWorkflowPath) + wasmFileBinary, err = cmdcommon.CompileWorkflowToWasm(resolvedWorkflowPath, false) spinner.Stop() if err != nil { ui.Error("Build failed:")