Ctrl K
ComunidadGuías…

bldr/util/gocompiler/build.go

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package gocompiler

import (
	"context"
	"path/filepath"
	"slices"
	"strings"
	"sync"
	"time"

	bldr_manifest "github.com/s4wave/spacewave/bldr/manifest"
	bldr_platform "github.com/s4wave/spacewave/bldr/platform"
	bldr_platform_go "github.com/s4wave/spacewave/bldr/platform/go"
	opt_wasm "github.com/s4wave/spacewave/bldr/util/opt/wasm"
	"github.com/sirupsen/logrus"
)

var tinyGoBuildMu sync.Mutex

// shouldDropWasmDebugSymbols reports whether the non-TinyGo Go build must pass
// -w -s. Diagnostic mode keeps the linker name section for CPU-profile
// symbolization, but only for WASM (non-native) output: native release builds
// always strip regardless of the diagnostic flag.
func shouldDropWasmDebugSymbols(isRelease, isNativeBuildPlatform, isWasmOutput, diagnostic bool) bool {
	if isNativeBuildPlatform {
		return isRelease
	}
	if isWasmOutput && diagnostic {
		return false
	}
	return isRelease || !isNativeBuildPlatform
}

// shouldRunWasmOpt reports whether the release Go wasm build should run the
// Binaryen wasm-opt pass. Diagnostic mode bypasses it so the linker name
// section (and any DWARF) survives for CPU-profile symbolization.
func shouldRunWasmOpt(diagnostic bool) (bool, error) {
	if diagnostic {
		return false, nil
	}
	return GoWasmOptimizeEnabled()
}

// ExecBuildEntrypoint executes building an entrypoint main package.
func ExecBuildEntrypoint(
	ctx context.Context,
	le *logrus.Entry,
	buildPlatform bldr_platform.Platform,
	buildType bldr_manifest.BuildType,
	workingPath,
	outBinPath string,
	enableCgo bool,
	useTinygo bool,
	buildTags []string,
	ldFlags []string,
) error {
	// Resolve the entrypoint build mode and wasm diagnostic setting.
	isRelease := buildType.IsRelease()
	isNativeBuildPlatform := buildPlatform.GetBasePlatformID() == bldr_platform.PlatformID_DESKTOP
	isWasmOutput := strings.HasSuffix(outBinPath, ".wasm")
	goWasmDiagnostic, err := GoWasmDiagnosticEnabled()
	if err != nil {
		return err
	}

	// Resolve the Go environment for the entrypoint platform.
	platformEnv, err := bldr_platform_go.PlatformToGoEnv(buildPlatform)
	if err != nil {
		return err
	}

	// always disable cgo if not native platform or not go compiler or webassembly
	if !isNativeBuildPlatform || isWasmOutput {
		enableCgo = false
	}

	// build tags
	buildTags = slices.Clone(buildTags)
	buildTags = append(buildTags, NewBuildTags(buildType)...)
	if useTinygo || !isNativeBuildPlatform || isWasmOutput {
		buildTags = append(buildTags, PureGoBuildTag, SQLLiteBuildTag)
	}

	// ldflags
	ldFlags = slices.Clone(ldFlags)

	// relative output path
	outBinPathRel, err := filepath.Rel(workingPath, outBinPath)
	if err != nil {
		return err
	}

	// args
	var cmd string
	var args []string
	if !useTinygo {
		cmd = "go"
		args = append([]string{
			"build",
			"-trimpath",
			"-o",
			outBinPathRel,
		}, GetDefaultArgs()...)

		// if release or not native platform drop debugging symbols.
		// Diagnostic mode keeps the linker name section for CPU-profile
		// symbolization; release/default output is unchanged.
		if shouldDropWasmDebugSymbols(isRelease, isNativeBuildPlatform, isWasmOutput, goWasmDiagnostic) {
			ldFlags = append(ldFlags, "-w", "-s")
		}

		args = append(args, "-tags="+strings.Join(buildTags, ","))
	} else {
		cmd = "tinygo"
		args, err = newTinyGoBuildArgs(buildPlatform, buildType, outBinPathRel, buildTags)
		if err != nil {
			return err
		}
	}

	// ldflags
	if len(ldFlags) != 0 {
		args = append(args, "-ldflags", strings.Join(ldFlags, " "))
	}

	// module path
	args = append(args, ".")

	// go build
	ecmd := NewGoCompilerCmd(ctx, cmd, args...)
	ecmd.Dir = workingPath
	if !useTinygo {
		if enableCgo {
			ecmd.Env = append(ecmd.Env, "CGO_ENABLED=1")
		} else {
			ecmd.Env = append(ecmd.Env, "CGO_ENABLED=0")
		}
		ecmd.Env = append(ecmd.Env, platformEnv...)
	}

	// Run the compiler and report the entrypoint build duration.
	timeStart := time.Now()
	if useTinygo {
		// TinyGo's LLVM pipeline can exhaust GitHub Linux runner memory when
		// multiple browser plugin compiles run inside one Bldr process.
		err = func() error {
			tinyGoBuildMu.Lock()
			defer tinyGoBuildMu.Unlock()
			return ExecGoCompiler(le, ecmd)
		}()
	} else {
		err = ExecGoCompiler(le, ecmd)
	}
	if err != nil {
		return err
	}
	le.
		WithField("compiler", cmd).
		WithField("dur", time.Since(timeStart).String()).
		Info("compiled plugin binary")

	// codesign the produced binary on darwin when BLDR_MACOS_SIGN_IDENTITY is set
	if !useTinygo && !isWasmOutput && slices.Contains(platformEnv, "GOOS=darwin") {
		if err := CodesignMacOS(ctx, le, outBinPath); err != nil {
			return err
		}
	}

	// az sign the produced binary on windows when BLDR_WINDOWS_SIGN_PROFILE is set
	if !useTinygo && !isWasmOutput && slices.Contains(platformEnv, "GOOS=windows") {
		if err := SignWindows(ctx, le, outBinPath); err != nil {
			return err
		}
	}

	// post-processing in release mode
	if isWasmOutput && isRelease {
		if useTinygo {
			tinygoDebugInfo, err := TinyGoDebugInfoEnabled()
			if err != nil {
				return err
			}
			if tinygoDebugInfo {
				le.Info("kept TinyGo wasm debug sections")
			} else {
				if err := opt_wasm.StripWasmDebugSections(ctx, le, workingPath, outBinPath); err != nil {
					return err
				}
			}
		} else {
			optimize, err := shouldRunWasmOpt(goWasmDiagnostic)
			if err != nil {
				return err
			}
			if !optimize {
				le.Info("skipped wasm-opt for Go wasm output")
				return nil
			}
			if err := opt_wasm.OptimizeWasmBinary(ctx, le, workingPath, outBinPath); err != nil {
				return err
			}
		}
	}

	return nil
}

const tinyGoInternalNoDWARFArg = "-internal-nodwarf"

func newTinyGoBuildArgs(
	buildPlatform bldr_platform.Platform,
	buildType bldr_manifest.BuildType,
	outBinPathRel string,
	buildTags []string,
) ([]string, error) {
	// Resolve the TinyGo target, options and browser import tags.
	tinygoPlat, err := bldr_platform_go.PlatformToTinyGoTarget(buildPlatform)
	if err != nil {
		return nil, err
	}
	tinygoArgs, err := GetDefaultTinygoArgs()
	if err != nil {
		return nil, err
	}
	tinygoDebugInfo, err := TinyGoDebugInfoEnabled()
	if err != nil {
		return nil, err
	}
	buildTags = slices.Clone(buildTags)
	if shouldUseBldrTinyGoJSImportBuildTag(buildPlatform, tinygoPlat) {
		buildTags = append(buildTags, BldrTinyGoJSImportBuildTag)
	}

	// Assemble the TinyGo command for the entrypoint output.
	args := append([]string{
		"build",
		"-o",
		outBinPathRel,
		"-target", tinygoPlat,
	}, tinygoArgs...)

	// Browser staging investigations sometimes need TinyGo DWARF symbols;
	// otherwise release and non-native builds drop debug info to keep wasm small.
	if !tinygoDebugInfo && (buildType.IsRelease() || buildPlatform.GetBasePlatformID() != bldr_platform.PlatformID_DESKTOP) {
		args = append(args, "-no-debug")
	}
	if !tinygoDebugInfo && shouldSkipTinyGoInternalDWARF(buildPlatform, buildType) {
		args = append(args, tinyGoInternalNoDWARFArg)
	}

	// Include the selected build tags in the TinyGo command.
	args = append(args, "-tags="+strings.Join(buildTags, " "))
	return args, nil
}

func shouldUseBldrTinyGoJSImportBuildTag(buildPlatform bldr_platform.Platform, tinygoPlat string) bool {
	return buildPlatform.GetBasePlatformID() == bldr_platform.PlatformID_WEB && tinygoPlat == "wasm"
}

func shouldSkipTinyGoInternalDWARF(buildPlatform bldr_platform.Platform, buildType bldr_manifest.BuildType) bool {
	if !buildType.IsRelease() {
		return false
	}
	np := bldr_platform.ToNativePlatform(buildPlatform)
	if np == nil {
		return false
	}
	return np.GetGOOS() == "js" && np.GetGOARCH() == "wasm"
}