lei/build/build.go
Erica Marigold fb8d0c1514
build: make build script more ergonomic
* Moved cloning functionality into its own `cloneSrc` function
* Start writing compiled data into CWD instead of user home dir, in
  order to prevent mangling between projects
* Introduce `LEI_LUAU_SRC` variable to allow users to provide a path to
  an already checked out luau source dir; when not set, a temporary
  directory is created and removed after compilation
* Do not panic if cmake build dir already exists, just skip creating in
  that case
* Automatically set required C includes path for commands
* Added warning to commands if the `.lei` directory is not present in
  the gitignore
2025-04-26 08:50:50 +01:00

152 lines
4.2 KiB
Go

package main
import (
"fmt"
"os"
"path"
"strings"
"github.com/gookit/color"
"golang.org/x/term"
)
const LUAU_VERSION = "0.634"
const ARTIFACT_NAME = "libLuau.VM.a"
func bail(err error) {
if err != nil {
panic(err)
}
}
func cloneSrc() string {
color.Blue.Println("> Cloning luau-lang/luau")
dir, tempDirErr := os.MkdirTemp("", "lei-build")
bail(tempDirErr)
// Clone down the Luau repo and checkout the required tag
Exec("git", "", "clone", "https://github.com/luau-lang/luau.git", dir)
Exec("git", dir, "checkout", LUAU_VERSION)
color.Green.Printf("> Cloned repo to%s\n\n", dir)
return dir
}
func buildVm(srcPath string, artifactPath string, includesDir string, cmakeFlags ...string) {
color.Blue.Println("> Compile libLuau.VM.a")
// Build the Luau VM using CMake
buildDir := path.Join(srcPath, "cmake")
buildDirErr := os.Mkdir(buildDir, os.ModePerm)
if buildDirErr != nil && !os.IsExist(buildDirErr) {
bail(buildDirErr)
}
defaultCmakeFlags := []string{"..", "-DCMAKE_BUILD_TYPE=RelWithDebInfo", "-DLUAU_EXTERN_C=ON", "-DCMAKE_POLICY_VERSION_MINIMUM=3.5"}
Exec("cmake", buildDir, append(defaultCmakeFlags, cmakeFlags...)...)
Exec("cmake", buildDir, "--build", ".", "--target Luau.VM", "--config", "RelWithDebInfo")
color.Green.Println("> Successfully compiled!\n")
// Copy the artifact to the artifact directory
artifactFile, artifactErr := os.ReadFile(path.Join(buildDir, ARTIFACT_NAME))
bail(artifactErr)
bail(os.WriteFile(artifactPath, artifactFile, os.ModePerm))
// Copy the header files into the includes directory
headerDir := path.Join(srcPath, "VM", "include")
headerFiles, headerErr := os.ReadDir(headerDir)
bail(headerErr)
for _, file := range headerFiles {
src := path.Join(headerDir, file.Name())
dest := path.Join(includesDir, file.Name())
headerContents, headerReadErr := os.ReadFile(src)
bail(headerReadErr)
os.WriteFile(dest, headerContents, os.ModePerm)
}
}
func main() {
workDir, workDirErr := os.Getwd()
bail(workDirErr)
artifactDir := path.Join(workDir, ".lei")
artifactPath := path.Join(artifactDir, ARTIFACT_NAME)
lockfilePath := path.Join(artifactDir, ".lock")
includesDir := path.Join(artifactDir, "includes")
bail(os.MkdirAll(includesDir, os.ModePerm)) // includesDir is the deepest dir, creates all
gitignore, gitignoreErr := os.ReadFile(".gitignore")
if gitignoreErr == nil && !strings.Contains(string(gitignore), ".lei") {
color.Yellow.Println("> WARN: The gitignore in the CWD does not include `.lei`, consider adding it")
}
// TODO: Args for clean build
args := os.Args[1:]
goArgs := []string{}
cmakeFlags := []string{}
features := []string{}
// TODO: maybe use env vars for this config instead
for _, arg := range args {
if arg == "--enable-vector4" {
features = append(features, "LUAU_VECTOR4")
// FIXME: This flag apparently isn't recognized by cmake for some reason
cmakeFlags = append(cmakeFlags, "-DLUAU_VECTOR_SIZE=4")
} else {
goArgs = append(goArgs, arg)
}
}
lockfileContents, err := os.ReadFile(lockfilePath)
if !os.IsNotExist(err) {
bail(err)
}
serFeatures := fmt.Sprintf("%v", features)
toCleanBuild := (string(lockfileContents) != serFeatures) || os.Getenv("LEI_CLEAN_BUILD") == "true"
if _, err := os.Stat(artifactPath); err == nil && !toCleanBuild {
fmt.Printf("[build] Using existing artifact at %s\n", artifactPath)
} else {
srcPath, notUnset := os.LookupEnv("LEI_LUAU_SRC")
if !notUnset {
srcPath = cloneSrc()
defer os.RemoveAll(srcPath)
}
buildVm(srcPath, artifactPath, includesDir, cmakeFlags...)
bail(os.WriteFile(lockfilePath, []byte(serFeatures), os.ModePerm))
}
buildTags := []string{}
if len(features) > 0 {
buildTags = append(buildTags, []string{"-tags", strings.Join(features, ",")}...)
}
w, _, termErr := term.GetSize(int(os.Stdout.Fd()))
bail(termErr)
fmt.Println(strings.Repeat("=", w))
subcommand := goArgs[0]
goArgs = goArgs[1:]
combinedArgs := append(buildTags, goArgs...)
cmd, _, _, _ := Command("go").
WithArgs(append([]string{subcommand}, combinedArgs...)...).
WithVar(
"CGO_LDFLAGS",
fmt.Sprintf("-L %s -lLuau.VM -lm -lstdc++", artifactDir),
).
WithVar("CGO_CFLAGS", fmt.Sprintf("-I%s", includesDir)).
WithVar("CGO_ENABLED", "1").
PipeAll(Forward).
ToCommand()
bail(cmd.Start())
bail(cmd.Wait())
}