Skip to content

Modernization Phase 3: Replace TinyGo embedded shell script with Starlark environment setup #42

@avrabe

Description

@avrabe

Problem

The TinyGo WebAssembly component rule contains a 50-line embedded bash script for environment setup, creating significant Windows compatibility issues and violating Bazel's hermetic build principles.

Current Implementation Issues

File: go/defs.bzl (Lines 245-294)

# 50-line embedded bash script in Starlark!
wrapper_content = """#!/bin/bash
set -euo pipefail

# Resolve absolute paths for TinyGo requirements
if [[ "{tinygo_root}" = /* ]]; then
    TINYGOROOT="{tinygo_root}"
else
    TINYGOROOT="$(pwd)/{tinygo_root}"
fi

# Validate TINYGOROOT exists and has expected structure
if [[ ! -d "$TINYGOROOT" ]]; then
    echo "Error: TINYGOROOT directory does not exist: $TINYGOROOT" >&2
    exit 1
fi

# Create GOCACHE directory if it doesn't exist
mkdir -p "{cache_path}"

# Build absolute PATH from relative tool paths
TOOL_PATHS=""
{tool_path_resolution}

# Set up environment with absolute paths
export TINYGOROOT
export GOCACHE="{cache_path}"
export CGO_ENABLED="0"
export GO111MODULE="off"
export GOPROXY="direct"
export HOME="{home_path}"
export TMPDIR="{tmp_path}"
export PATH="$TOOL_PATHS"

# Execute TinyGo with resolved paths
exec "$@"
""".format(...)

Additional Issues

  • Complex path resolution function (Lines 26-44) generates more shell code
  • Tool path discovery logic embedded in shell instead of Starlark
  • Environment variable construction done via string formatting instead of structured approach

Impact

  • Windows incompatibility: Bash script won't run on Windows without WSL
  • Complex debugging: 50+ lines of shell script are hard to debug when TinyGo builds fail
  • Hermetic violations: Relies on system bash, shell utilities, and filesystem layout
  • Maintenance burden: Shell logic embedded in Starlark is harder to test and maintain
  • Security concerns: Complex shell scripts increase attack surface

Solution

Replace the embedded shell script with structured Starlark logic and ctx.actions.write for environment setup.

Implementation Plan

  1. Replace shell path resolution with Starlark:

    def _build_tinygo_environment(ctx, tinygo_toolchain, tool_paths):
        """Build TinyGo environment using Starlark - THE BAZEL WAY"""
        
        # Calculate TINYGOROOT using Starlark path manipulation
        tinygo_root = "/".join(tinygo_toolchain.tinygo.path.split("/")[:-2])
        
        # Build PATH using Starlark list operations  
        path_components = [tool.dirname for tool in tool_paths] + ["/usr/bin", "/bin"]
        
        # Create environment file using ctx.actions.write
        env_content = """export TINYGOROOT="{}"
    export GOCACHE="{}"
    export PATH="{}"
    export CGO_ENABLED="0"
    """.format(tinygo_root, cache_path, ":".join(path_components))
        
        env_file = ctx.actions.declare_file(ctx.label.name + "_env.sh")
        ctx.actions.write(output=env_file, content=env_content, is_executable=True)
        return env_file
  2. Alternative: Use ctx.actions.run with env parameter:

    # Instead of wrapper script, pass environment directly
    ctx.actions.run(
        executable = tinygo,
        arguments = tinygo_args,
        inputs = inputs,
        outputs = outputs,
        env = {
            "TINYGOROOT": tinygo_root,
            "GOCACHE": cache_path,
            "PATH": ":".join(path_components),
            "CGO_ENABLED": "0",
        },
        mnemonic = "TinyGoCompile"
    )
  3. Path validation in Starlark:

    def _validate_tinygo_environment(ctx, tinygo_toolchain):
        """Validate TinyGo environment using Starlark checks"""
        
        tinygo_binary = tinygo_toolchain.tinygo
        if not tinygo_binary:
            fail("TinyGo binary not found in toolchain")
        
        # Use repository_ctx for file existence checks during toolchain setup
        # Runtime validation via separate action if needed

Files to Update

  • go/defs.bzl - Replace embedded shell script with Starlark logic
  • go/defs.bzl - Simplify _build_tool_path_resolution function
  • go/defs.bzl - Update _compile_tinygo_module to use structured environment

Research Required

  • Test TinyGo's specific environment requirements
  • Verify that ctx.actions.run env parameter works for complex toolchain setup
  • Check if any TinyGo-specific shell behavior needs preservation

Testing

  • Test Go component compilation on Windows, macOS, and Linux
  • Verify TinyGo cache handling works correctly
  • Test with various Go module structures and dependencies
  • Ensure wasm-opt integration still works
  • Performance testing for large Go projects

Acceptance Criteria

  • No embedded bash scripts in go/defs.bzl
  • All environment setup done via Starlark + ctx.actions.write or env parameter
  • Path resolution logic moved to Starlark functions
  • Cross-platform compatibility verified (especially Windows)
  • All TinyGo compilation features preserved
  • Performance equal or better than current implementation
  • Go component examples continue to work

Priority

MEDIUM-HIGH - Major Windows compatibility improvement, but more complex than other phases.

Dependencies

Independent issue, but benefits from completion of Issues #40 and #41 first.

Migration Strategy

This change affects the core Go compilation pipeline, so should be implemented carefully with comprehensive testing on all platforms.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions