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", ) } }