gutter_runner/builder/builder.odin
2025-05-24 22:31:06 +04:00

347 lines
7.0 KiB
Odin

package builder
import "core:flags"
import "core:fmt"
import "core:log"
import "core:io"
import os "core:os/os2"
import "core:path/filepath"
import "core:slice"
import "core:strings"
import "core:thread"
Build_Variant :: enum {
Hot_Reload,
Desktop,
Web,
}
Options :: struct {
variant: Build_Variant `usage:"Variant of the build"`,
optimize: bool `args:"name=opt",usage:"Enable compiler optimizations"`,
debug: bool `usage:"Enable debug symbols"`,
force: bool `usage:"When enabled dependencies will be cleaned and rebuilt"`,
tracy: bool `usage:"Enable tracy profiler"`,
run: bool,
}
temp_concat :: proc(strs: ..[]string) -> []string {
result := slice.concatenate(strs, context.temp_allocator)
return result
}
build_deps :: proc(opts: Options) {
log.infof("build_deps")
force := opts.force
shared := opts.variant == .Hot_Reload
// Raylib
{
cwd := "./libs/raylib"
out_dir := shared ? "zig-out-shared" : "zig-out-static"
remove_all(fmt.tprintf("./libs/raylib/%s", out_dir))
if force {
remove_all("./libs/raylib/.zig-cache")
}
target := opts.variant == .Web ? "wasm32-emscripten" : "native"
run_cmd(
{
"zig",
"build",
"-p",
out_dir,
fmt.tprintf("-Dshared=%v", shared),
fmt.tprintf("-Dtarget=%s", target),
},
cwd,
)
}
// Physfs
{
cwd := "./libs/physfs"
file_name := shared ? "libphysfs.so" : "libphysfs.a"
is_built := os.is_dir("./libs/physfs/build")
if force {
remove_all("./libs/physfs/build")
}
prepare_cmd := []string {
"cmake",
"-B",
"build",
fmt.tprintf("-DPHYSFS_BUILD_SHARED=%v", shared),
"-DCMAKE_BUILD_TYPE=MinSizeRel",
"./physfs",
}
build_cmd := []string{"cmake", "--build", "build", "--config", "MinSizeRel"}
if !is_built || force {
if opts.variant == .Web {
run_cmd(temp_concat({"emcmake.bat"}, prepare_cmd), cwd)
run_cmd(temp_concat({"emmake.bat"}, build_cmd), cwd)
} else {
run_cmd(prepare_cmd, cwd)
run_cmd(build_cmd, cwd)
}
}
}
// Tracy
if opts.tracy {
assert(opts.variant != .Web)
cwd := "./libs/tracy"
when ODIN_OS == .Windows {
TRACY_NAME_SHARED :: "tracy.dll"
TRACY_NAME_STATIC :: "tracy.lib"
} else when ODIN_OS == .Linux {
TRACY_NAME_SHARED :: "tracy.so"
TRACY_NAME_STATIC :: "tracy.a"
} else when ODIN_OS == .Darwin {
TRACY_NAME_SHARED :: "tracy.dynlib"
TRACY_NAME_STATIC :: "tracy.a"
}
file_path := fmt.tprintf("./libs/tracy/%s", shared ? TRACY_NAME_SHARED : TRACY_NAME_STATIC)
is_built := os.is_file(file_path)
if is_built && force {
remove_file(file_path)
}
if !is_built || force {
run_cmd(
slice.concatenate(
[][]string {
{
"zig",
"c++",
"-std=c++11",
"-DTRACY_ENABLE",
"-O2",
"vendor/tracy/public/TracyClient.cpp",
"-lws2_32",
"-ldbghelp",
},
shared ? {"-shared", "-o", TRACY_NAME_SHARED} : {"-c", "-o", "tracy.o"},
},
context.temp_allocator,
),
cwd,
)
if !shared {
run_cmd({"zig", "ar", "rc", TRACY_NAME_STATIC, "tracy.o"}, cwd)
}
}
}
}
COMMON_FLAGS :: []string {
"-collection:libs=./libs",
"-collection:common=./common",
"-collection:game=./game",
"-strict-style",
"-vet",
}
setup_emsdk_env :: proc() {
when ODIN_OS == .Windows {
PATH_ENV_DELIMITER :: ";"
} else {
PATH_ENV_DELIMITER :: ":"
}
PATHS :: [3]string {
"./libs/emsdk/upstream/bin",
"./libs/emsdk/upstream/emscripten",
"./libs/emsdk/ninja/git-release_64bit/bin",
}
paths: [len(PATHS)]string
for path, i in PATHS {
paths[i] = absolute_path(path)
}
set_env(
"PATH",
strings.join(
{paths[0], paths[1], paths[2], os.get_env("PATH", context.temp_allocator)},
PATH_ENV_DELIMITER,
context.temp_allocator,
),
)
set_env("EMSDK", absolute_path("./libs/emsdk"))
}
main :: proc() {
context.logger = log.create_console_logger()
opts := Options {
tracy = true,
debug = true,
run = true,
}
flags.parse_or_exit(&opts, os.args, .Unix, context.temp_allocator)
if opts.variant == .Web {
log.warnf("tracy is not supported on Web")
opts.tracy = false
setup_emsdk_env()
}
tracy_flag: []string = opts.tracy ? {"-define:TRACY_ENABLE=true"} : {}
debug_flag: []string = opts.debug ? {"-debug"} : {}
optimize_flag: []string = opts.optimize ? {"-o:speed"} : {}
build_deps(opts)
switch opts.variant {
case .Hot_Reload:
remove_all("./build/hotreload")
mkdir_all("./build/hotreload")
is_main_built := os.is_file("./build/hotreload/game.bin")
if !is_main_built || opts.force {
run_cmd(
temp_concat(
[]string {
"odin",
"build",
"main_hot_reload",
"-out:./build/hotreload/game.bin",
},
debug_flag,
tracy_flag,
optimize_flag,
COMMON_FLAGS,
),
"",
)
}
build_game_cmd := temp_concat(
[]string {
"odin",
"build",
"game",
"-define:RAYLIB_SHARED=true",
"-define:PHYSFS_SHARED=true",
"-build-mode:dll",
"-out:./build/hotreload/game_tmp.so",
},
tracy_flag,
debug_flag,
optimize_flag,
COMMON_FLAGS,
)
build_game_lib :: proc(build_cmd: []string) {
run_cmd(
build_cmd,
"",
)
rename("./build/hotreload/game_tmp.so", "./build/hotreload/game.so")
}
build_game_lib(build_game_cmd)
if opts.run {
thread_ctx := context
thread_ctx.user_ptr = &build_game_cmd
thrd := thread.create_and_start(proc() {
build_game_cmd := (cast(^[]string)context.user_ptr)^
input := os.to_reader(os.stdin)
buf: [1024]u8
for {
n, err := io.read(input, buf[:])
for cmd in buf[0:n] {
switch cmd {
case 'r', 'R':
build_game_lib(build_game_cmd)
case:
}
}
if err == .EOF {
break
}
handle_error(err)
}
}, thread_ctx)
run_cmd({"./build/hotreload/game.bin"}, "")
thread.join(thrd)
}
case .Desktop:
remove_all("./build/desktop")
mkdir_all("./build/desktop")
cmd := slice.concatenate(
[][]string {
[]string{"odin", "build", "main_release", "-out:./build/desktop/game.exe"},
tracy_flag,
debug_flag,
optimize_flag,
COMMON_FLAGS,
},
context.temp_allocator,
)
run_cmd(cmd, "")
case .Web:
remove_all("./build/web")
mkdir_all("./build/web")
odin_root := run_cmd({"odin", "root"}, "")
cmd := slice.concatenate(
[][]string {
[]string {
"odin",
"build",
"main_web",
"-target:js_wasm32",
"-build-mode:obj",
"-out:build/web/game",
},
COMMON_FLAGS,
debug_flag,
optimize_flag,
},
)
run_cmd(cmd, "")
run_cmd(
{
"emcc.bat",
"-g",
"-o",
"build/web/index.html",
"build/web/game.wasm.o",
"libs/raylib/zig-out-static/lib/libraylib.a",
"libs/physfs/build/libphysfs.a",
"-sUSE_GLFW=3",
"-sWASM_BIGINT",
"-sWARN_ON_UNDEFINED_SYMBOLS=0",
"-sALLOW_MEMORY_GROWTH",
"-sASSERTIONS",
"--shell-file",
"main_web/index_template.html",
"--preload-file",
"assets",
},
"",
)
remove_file("./build/web/game.wasm.o")
copy_file(
filepath.join(
{odin_root, "core", "sys", "wasm", "js", "odin.js"},
context.temp_allocator,
),
"./build/web/odin.js",
)
}
}