beginning conversion from cmake to buildit

This commit is contained in:
jacob 2024-05-16 23:50:57 -05:00
parent 27d6acadc7
commit ed9036c373
3 changed files with 590 additions and 435 deletions

View File

@ -1,307 +0,0 @@
cmake_minimum_required(VERSION 3.25.1)
project(powerplay)
# Options below are laid out so that running cmake with all "OFF" results in the "release" / "user" build
option(RTC "Should the build compile with runtime checks enabled (asserts, asan, etc.) - REQUIRES CRTLIB=ON" OFF)
option(ASAN "Should the build compile with the address sanitizer enabled (asserts, asan, etc.) - REQUIRES CRTLIB=ON" OFF)
option(CRTLIB "Should the build link with the CRTLIB" OFF)
option(DEBINFO "Should the build compile with debug info" OFF)
option(DEVELOPER "Should the build compile with developer mode enabled" OFF)
option(PROFILING "Should the build compile with profiling enabled - REQUIRES CRTLIB=ON, MSVC=OFF" OFF)
option(UNOPTIMIZED "Should the build compile with optimization disabled" OFF)
option(MSVC "Should the build compile using MSVC" OFF)
################################################################################
# Source files
################################################################################
# Glob source files
file(GLOB_RECURSE sources CONFIGURE_DEPENDS src/*.c src/*.cpp src/*.h)
# Filter platform specific
list(FILTER sources EXCLUDE REGEX "sys_|renderer_|playback_|mp3_|ttf_")
if(PLATFORM_WIN32)
set(sources ${sources} src/sys_win32.c)
set(sources ${sources} src/renderer_d3d11.c)
set(sources ${sources} src/playback_wasapi.c)
set(sources ${sources} src/mp3_mmf.c)
set(sources ${sources} src/ttf_dwrite.cpp)
set(sources ${sources} .natvis)
endif()
# Add tracy
list(FILTER sources EXCLUDE REGEX "third_party")
if(PROFILING)
set(sources ${sources} src/third_party/tracy/TracyClient.cpp)
endif()
list(FILTER sources EXCLUDE REGEX "WIP")
################################################################################
# Resource embedding
################################################################################
set(inc_dependencies "")
# Only archive & embed "res" dir if not compiling a developer build
if(NOT DEVELOPER)
# Generate resource archive
set(resource_archive_path "${CMAKE_BINARY_DIR}/res.tar")
file(GLOB_RECURSE resource_sources CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/res/*)
add_custom_command(
OUTPUT ${resource_archive_path}
COMMAND cmake -E tar cvf ${resource_archive_path} .
DEPENDS ${resource_sources}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/res
)
list(APPEND inc_dependencies ${resource_archive_path})
endif()
# Generate shaders archive
set(shaders_archive_path "${CMAKE_BINARY_DIR}/shaders.tar")
file(GLOB_RECURSE shader_sources CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/src/shaders/*)
add_custom_command(
OUTPUT ${shaders_archive_path}
COMMAND cmake -E tar cvf ${shaders_archive_path} .
DEPENDS ${shader_sources}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src/shaders
)
list(APPEND inc_dependencies ${shaders_archive_path})
set_source_files_properties(src/inc.c OBJECT_DEPENDS "${inc_dependencies}")
################################################################################
# RC file (windows)
################################################################################
set(rc_res_sources "")
if(PLATFORM_WIN32)
set(RC_PATH "${CMAKE_BINARY_DIR}/rc.rc")
set(RC_RES_PATH "${CMAKE_BINARY_DIR}/rc.res")
# Add icon resource to .rc
set(ICON_SOURCE_PATH "${CMAKE_SOURCE_DIR}/icon.ico")
set(ICON_COPY_PATH "${CMAKE_BINARY_DIR}/icon.ico")
if(EXISTS ${ICON_SOURCE_PATH})
# Copy icon to output dir
add_custom_command(
OUTPUT ${ICON_COPY_PATH}
COMMAND ${CMAKE_COMMAND} -E copy ${ICON_SOURCE_PATH} ${ICON_COPY_PATH}
DEPENDS ${ICON_SOURCE_PATH}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
# Insert icon into .rc
add_custom_command(
OUTPUT ${RC_PATH}
COMMAND break > ${RC_PATH}
COMMAND echo IDI_ICON ICON DISCARDABLE icon.ico >> ${RC_PATH}
DEPENDS ${ICON_COPY_PATH}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
# Compile rc.rc -> rc.res
add_custom_command(
OUTPUT ${RC_RES_PATH}
COMMAND llvm-rc ${RC_PATH}
DEPENDS ${ICON_COPY_PATH} ${RC_PATH}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
add_custom_target(build_rc_res ALL DEPENDS ${RC_RES_PATH})
# Add to list of res_sources
set(rc_res_sources ${rc_res_sources} ${RC_RES_PATH})
endif()
endif()
################################################################################
# Executable
################################################################################
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# Add executable
add_executable(powerplay_exe WIN32 ${sources})
set_target_properties(
powerplay_exe PROPERTIES
C_STANDARD 99
OUTPUT_NAME "PowerPlay.exe"
)
# Add precompiled header
target_precompile_headers(powerplay_exe PRIVATE src/common.h)
################################################################################
# Compiler flags
################################################################################
# TODO:
# Enable -
# -Wconversion \
# -Wno-sign-conversion \
if (MSVC)
set(COMPILER_WARNINGS "")
set(COMPILER_AND_LINKER_FLAGS)
set(COMPILER_FLAGS)
set(LINKER_FLAGS "${rc_res_sources} /subsystem:windows")
set(C_VERSION "/std:c11")
set(CPP_VERSION "/std:c++20")
else()
set(COMPILER_WARNINGS " \
-Weverything -Werror \
-Wframe-larger-than=65536 \
\
-Wno-unused-macros -Wno-gnu-zero-variadic-macro-arguments -Wno-documentation \
-Wno-old-style-cast -Wno-conversion -Wno-sign-conversion \
-Wno-declaration-after-statement -Wno-extra-semi -Wno-extra-semi-stmt \
-Wno-bad-function-cast -Wno-class-varargs -Wno-unreachable-code-break \
-Wno-cast-align -Wno-float-equal -Wno-zero-as-null-pointer-constant \
-Wno-cast-qual -Wno-missing-noreturn -Wno-missing-field-initializers \
-Wno-missing-braces -Wno-initializer-overrides \
-Wno-c99-extensions -Wno-c++98-compat-pedantic -Wno-c++98-compat \
-Wno-switch-enum -Wno-switch-default \
-Wno-reserved-identifier -Wno-reserved-macro-identifier \
-Wno-unsafe-buffer-usage -Wno-writable-strings \
")
# -Wno-unused-vfariable -Wno-unused-but-set-variable -Wno-unused-parameter
set(COMPILER_AND_LINKER_FLAGS "
-fuse-ld=lld-link \
-nostdlib \
-fno-strict-aliasing \
-fno-finite-loops \
-fwrapv \
-msse4.2 \
")
set(COMPILER_FLAGS)
set(LINKER_FLAGS " \
${rc_res_sources} \
-fno-strict-aliasing \
-fno-finite-loops \
-fwrapv \
-msse4.2 \
")
set(C_VERSION "-std=c99")
set(CPP_VERSION "-std=c++20")
endif()
# RTC (Runtime checks)
if (RTC)
if (NOT CRTLIB)
message(FATAL_ERROR "CRTLIB (C runtime library) Must be enabled when compiling with RTC (runtime checks)")
endif()
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} -DRTC=1")
if (MSVC)
if (NOT ASAN)
# Enable RTC (not compatible with ASAN)
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} /RTCcsu")
endif()
else()
# Enable UBSan
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} -fsanitize=undefined -fsanitize-trap=all")
# set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} -fsanitize=undefined -DRTC=1")
endif()
endif()
# CRTLIB (C runtime library)
if (CRTLIB)
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} -DCRTLIB=1")
if (RTC)
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} -D_DEBUG -D_MT -D_DLL")
if (MSVC)
set(LINKER_FLAGS "${LINKER_FLAGS} msvcrtd.lib ucrtd.lib msvcprtd.lib vcruntime.lib")
else()
set(LINKER_FLAGS "${LINKER_FLAGS} -lmsvcrtd -lucrtd -lmsvcprtd -lvcruntimed")
endif()
else()
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} -D_MT -D_DLL")
if (MSVC)
set(LINKER_FLAGS "${LINKER_FLAGS} msvcrt.lib ucrt.lib msvcprt.lib vcruntime.lib ")
else()
set(LINKER_FLAGS "${LINKER_FLAGS} -lmsvcrt -lucrt -lmsvcprt -lvcruntime")
endif()
endif()
else()
if (MSVC)
message(FATAL_ERROR "TODO")
else()
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} -mno-stack-arg-probe -fno-builtin")
endif()
endif()
# Optimization
if (UNOPTIMIZED)
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} -DUNOPTIMIZED=1")
if (MSVC)
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} /Od")
else()
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} -O0")
endif()
else()
if (MSVC)
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} /O2 /LTCG")
else()
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} -O3 -flto")
endif()
endif()
# Debug info
if (DEBINFO)
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} -DDEBINFO=1")
if (MSVC)
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} /Zi")
else()
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} -g")
endif()
endif()
# ASAN
if (ASAN)
if (NOT CRTLIB)
message(FATAL_ERROR "CRTLIB (C runtime library) Must be enabled when compiling with ASAN")
endif()
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} -DASAN=1")
if (MSVC)
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} /fsanitize=address")
else()
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} -fsanitize=address -shared-libasan")
endif()
endif()
# Developer mode
if(DEVELOPER)
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} -DDEVELOPER=1")
endif()
# Profiling
if(PROFILING)
if (NOT CRTLIB)
message(FATAL_ERROR "CRTLIB (C runtime library) Must be enabled when compiling with PROFILING")
endif()
if (MSVC)
message(FATAL_ERROR "MSVC not supported with PROFILING enabled (Profiling relies on Clang attributes)")
endif()
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} -DPROFILING=1")
# Tracy flags
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} -DTRACY_ENABLE=1")
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} -DTRACY_CALLSTACK=5")
set(COMPILER_AND_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} -DTRACY_NO_SAMPLING -DTRACY_NO_SYSTEM_TRACING -DTRACY_NO_CALLSTACK")
# Disable warnings when compiling tracy client
set(COMPILER_WARNINGS "-Wno-everything")
endif()
set(CMAKE_C_FLAGS "${COMPILER_AND_LINKER_FLAGS} ${COMPILER_FLAGS} ${COMPILER_WARNINGS} ${C_VERSION}")
set(CMAKE_CXX_FLAGS "${COMPILER_AND_LINKER_FLAGS} ${COMPILER_FLAGS} ${COMPILER_WARNINGS} ${CPP_VERSION}")
set(CMAKE_EXE_LINKER_FLAGS "${COMPILER_AND_LINKER_FLAGS} ${LINKER_FLAGS}")

128
build.bat
View File

@ -1,128 +0,0 @@
@echo off
setlocal
:: Description of command line arguments (disabled by default):
:: "debug" The target is intended to run in a debugger with debug info and optimizations disabled
:: "developer" The target will include all developer tooling
:: "profiling" The target will be compiled with profiling markup
:: "asan" The target will compile with address sanitizer enabled
:: "msvc" Compile with MSVC instead of Clang
for %%a in (%*) do set "%%a=1"
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Verify environment
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
if "%Platform%" neq "x64" (
echo ERROR: Platform is not "x64" - please run this from the MSVC x64 native tools command prompt.
exit /b 1
)
if "%msvc%" == "1" (
where /q cl.exe || (
echo ERROR: "cl.exe" not found.
exit /b 1
)
) else (
where /q clang.exe || (
echo ERROR: "clang.exe" not found.
exit /b 1
)
where /q lld-link || (
echo ERROR: "lld-link.exe" not found.
exit /b 1
)
)
where /q ninja.exe || (
echo ERROR: "ninja.exe" not found.
exit /b 1
)
where /q cmake || (
echo ERROR: "cmake.exe" not found.
exit /b 1
)
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Choose configuration from args
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Must explicitly pass disabled CMake options or else missing options are
:: assumed to be equal to the value from the last build
set cmake_options_zero=-DRTC=0 -DASAN=0 -DCRTLIB=0 -DDEBINFO=0 -DDEVELOPER=0 -DPROFILING=0 -DUNOPTIMIZED=0 -DMSVC=0
set cmake_options_enabled=
echo ========================================
if "%msvc%" == "1" (
echo [Msvc]
set cmake_options_enabled=%cmake_options_enabled% -DMSVC=1
) else (
echo [Clang]
)
if "%debug%" == "1" (
echo [Debug build]
set cmake_options_enabled=%cmake_options_enabled% -DRTC=1 -DCRTLIB=1 -DDEBINFO=1 -DUNOPTIMIZED=1
) else (
echo [Release build]
)
if "%developer%" == "1" (
echo [Developer build]
set cmake_options_enabled=%cmake_options_enabled% -DDEVELOPER=1
) else (
echo [User build]
)
if "%profiling%" == "1" (
echo [Profiling enabled]
set cmake_options_enabled=%cmake_options_enabled% -DPROFILING=1 -DCRTLIB=1 -DDEBINFO=1
)
if "%asan%" == "1" (
echo [Address sanitizer enabled]
set cmake_options_enabled=%cmake_options_enabled% -DASAN=1 -DCRTLIB=1 -DDEBINFO=1
)
echo ========================================
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Build
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
if not exist "build" mkdir build
if "%msvc%" == "1" (
cmake -H. -G Ninja -B build %cmake_options_zero% %cmake_options_enabled%^
-DCMAKE_C_COMPILER:PATH="cl.exe"^
-DCMAKE_CXX_COMPILER:PATH="cl.exe"^
-DPLATFORM_WIN32=1
) else (
cmake -H. -G Ninja -B build %cmake_options_zero% %cmake_options_enabled%^
-DCMAKE_C_COMPILER:PATH="clang.exe"^
-DCMAKE_CXX_COMPILER:PATH="clang.exe"^
-DCMAKE_C_COMPILER_ID="Clang"^
-DCMAKE_CXX_COMPILER_ID="Clang"^
-DCMAKE_SYSTEM_NAME="Generic"^
-DPLATFORM_WIN32=1
)
if NOT %errorlevel% == 0 (
echo.
echo ERROR: Configuration failed
exit /b 1
)
cmake --build build
:: cmake --build build -v
if NOT %errorlevel% == 0 (
echo.
echo ERROR: Build failed
exit /b 1
)

590
build.c Normal file
View File

@ -0,0 +1,590 @@
#define Rtc 1
#include "buildit.h"
#include <stdbool.h>
/* Capabilities:
*
* Run commands. Return result of command.
*
*
*/
/* ========================== *
* Globals
* ========================== */
/* ========================== *
* Args
* ========================== */
#define ARGS_XLIST(X) \
X(CLANG, "clang", "Compile with clang") \
X(MSVC, "msvc", "Compile with msvc") \
X(RTC, "rtc", "Should the build compile with runtime checks enabled (asserts, asan, etc.) - Requires crtlib") \
X(ASAN, "asan", "Should the build compile with the address sanitizer enabled (asserts, asan, etc.) - Requires crtlib") \
X(CRTLIB, "crtlib", "Should the build link with the CRTLIB") \
X(DEBINFO, "debinfo", "Should the build compile with debug info") \
X(DEVELOPER, "developer", "Should the build compile with developer mode enabled") \
X(PROFILING, "profiling", "Should the build compile with profiling enabled - Requires crtlib, clang") \
X(UNOPTIMIZED, "unoptimized", "Should the build compile with optimization disabled")
/* Make args global variables */
#define X(id, str, desc) bool id = 0;
ARGS_XLIST(X)
#undef X
/* Setup arg table */
#define X(id, str, desc) _ARGID_ ## id,
enum ArgId {
ARGS_XLIST(X)
ARGS_COUNT
};
#undef X
typedef struct ArgEntry ArgEntry;
struct ArgEntry {
String name;
bool *var;
};
#define X(id, str, desc) [_ARGID_ ## id] = { .name = Lit(str), .var = &id },
ArgEntry arg_entries[ARGS_COUNT] = {
ARGS_XLIST(X)
};
#undef X
/* ========================== *
* Util
* ========================== */
void Error(String msg)
{
SH_PrintF(Lit("ERROR: %F\n"), FmtStr(msg));
}
/* ========================== *
* Compile command
* ========================== */
typedef struct CompileCommandListNode CompileCommandListNode;
struct CompileCommandListNode {
String comp_command;
String link_file_path;
CompileCommandListNode *next;
CompileCommandListNode *prev;
};
typedef struct CompileCommandList CompileCommandList;
struct CompileCommandList {
CompileCommandListNode *first;
CompileCommandListNode *last;
Size count;
};
void CompileCommandListAppend(Arena *arena, CompileCommandList *l, String comp_command, String link_file_path)
{
CompileCommandListNode *n = ArenaPush(arena, CompileCommandListNode);
n->comp_command = comp_command;
n->link_file_path = link_file_path;
DllPushBack(l->first, l->last, n);
++l->count;
}
/* ========================== *
* Rc include
* ========================== */
typedef struct RcIncludeListNode RcIncludeListNode;
struct RcIncludeListNode {
D_Tag tag;
String rc_type;
RcIncludeListNode *next;
RcIncludeListNode *prev;
};
typedef struct RcIncludeList RcIncludeList;
struct RcIncludeList {
RcIncludeListNode *first;
RcIncludeListNode *last;
Size count;
};
void RcIncludeListAppend(Arena *arena, RcIncludeList *l, D_Tag tag, String rc_type)
{
RcIncludeListNode *n = ArenaPush(arena, RcIncludeListNode);
n->tag = tag;
n->rc_type = rc_type;
DllPushBack(l->first, l->last, n);
++l->count;
}
/* ========================== *
* Build
* ========================== */
void OnBuild(StringList args_list)
{
Arena arena = ArenaAlloc(Gigabyte(64));
CompileCommandList compile_command_list = { 0 };
String obj_file_extension = PlatformWindows ? Lit("obj") : Lit("o");
D_Tag executable = D_FileTagFromPath(&arena, Lit("build/bin/PowerPlay.exe"));
D_Tag pch_input = D_FileTagFromPath(&arena, Lit("src/common.h"));
D_Tag pch_c_output = D_FileTagFromPath(&arena, Lit("build/common_c.pch"));
D_Tag pch_cpp_output = D_FileTagFromPath(&arena, Lit("build/common_cpp.pch"));
/* ========================== *
* Unpack command line args
* ========================== */
for (StringListNode *n = args_list.first; n; n = n->next) {
String arg_name = n->string;
for (int i = 0; i < ArrayCount(arg_entries); ++i) {
ArgEntry *entry = &arg_entries[i];
if (StringEqual(arg_name, entry->name)) {
*entry->var = 1;
}
}
}
if (MSVC && CLANG) {
Error(Lit("Msvc & clang arguments cannot both be set"));
OS_Exit(1);
} else if (!(MSVC || CLANG)) {
CLANG = 1;
}
if (MSVC) CLANG = 0;
if (CLANG) MSVC = 0;
/* ========================== *
* Determine compiler args
* ========================== */
String final_c_compile_args_fmt = { 0 };
String final_cpp_compile_args_fmt = { 0 };
String final_pch_c_compile_args_fmt = { 0 };
String final_pch_cpp_compile_args_fmt = { 0 };
String final_link_args_fmt = { 0 };
{
StringList warnings = { 0 };
StringList compile_and_link_args = { 0 };
StringList compile_args = { 0 };
StringList c_compile_args = { 0 };
StringList cpp_compile_args = { 0 };
StringList pch_c_compile_args = { 0 };
StringList pch_cpp_compile_args = { 0 };
StringList link_args = { 0 };
#if 0
#if Rtc
/* ========================== *
* Verify environment
* ========================== */
{
I32 error = SH_RunCommand(Lit("where clang-cl.exe"), true);
if (error) {
StringListAppend(&arena, &compile_args, Lit("msdev &&"));
StringListAppend(&arena, &link_args, Lit("msdev &&"));
}
}
#if 0
{
/* TODO: Check for MSVC cl & rc */
if (SH_RunCommand(Lit("where clang-cl.exe"), true)) {
Error(Lit("Could not locate clang-cl.exe"));
OS_Exit(1);
}
if (SH_RunCommand(Lit("where llvm-rc.exe"), true)) {
Error(Lit("Could not locate llvm-rc.exe"));
OS_Exit(1);
}
}
#endif
#endif
#endif
if (CLANG) {
#if 1
//StringListAppend(&arena, &compile_args, Lit("clang -c"));
//StringListAppend(&arena, &link_args, Lit("lld-link"));
//StringListAppend(&arena, &c_compile_args, Lit("msdev && clang -c -o %F %F -std=c99"));
//StringListAppend(&arena, &pch_c_compile_args, Lit("msdev && clang -c -o %F %F -x c-header -std=c99"));
//StringListAppend(&arena, &cpp_compile_args, Lit("msdev && clang -c -o %F %F -std=c++20"));
//StringListAppend(&arena, &pch_cpp_compile_args, Lit("msdev && clang -c -o %F %F -x c++-header -std=c++20"));
StringListAppend(&arena, &c_compile_args, Lit("msdev && clang -xc -std=c99 -c %F -o %F"));
StringListAppend(&arena, &pch_c_compile_args, Lit("msdev && clang -xc-header -std=c99 -c %F -o %F"));
StringListAppend(&arena, &cpp_compile_args, Lit("msdev && clang -xc++ -std=c++20 -c %F -o %F"));
StringListAppend(&arena, &pch_cpp_compile_args, Lit("msdev && clang -xc++-header -std=c++20 -c %F -o %F"));
StringListAppend(&arena, &link_args, Lit("msdev && clang %F -o %F"));
#else
StringListAppend(&arena, &compile_args, Lit("clang -c"));
StringListAppend(&arena, &link_args, Lit("lld-link"));
StringListAppend(&arena, &c_compile_args, Lit("-std=c99"));
StringListAppend(&arena, &pch_c_compile_args, Lit("-x c-header -std=c99"));
StringListAppend(&arena, &cpp_compile_args, Lit("-std=c++20"));
StringListAppend(&arena, &pch_cpp_compile_args, Lit("-x c++-header -std=c++20"));
StringListAppend(&arena, &compile_args, Lit("-o %F %F"));
StringListAppend(&arena, &link_args, Lit("%F"));
#endif
//"-fuse-ld=lld-link"
StringListAppend(&arena,
&compile_and_link_args,
Lit("-fuse-ld=lld-link "
"-nostdlib "
"-fno-strict-aliasing "
"-fno-finite-loops "
"-fwrapv "
"-msse4.1 "
"-msse4.2 "));
StringListAppend(&arena,
&warnings,
Lit("-Weverything -Werror "
"-Wframe-larger-than=65536 "
"-Wno-unused-macros -Wno-gnu-zero-variadic-macro-arguments -Wno-documentation "
"-Wno-old-style-cast -Wno-conversion -Wno-sign-conversion "
"-Wno-declaration-after-statement -Wno-extra-semi -Wno-extra-semi-stmt "
"-Wno-bad-function-cast -Wno-class-varargs -Wno-unreachable-code-break "
"-Wno-cast-align -Wno-float-equal -Wno-zero-as-null-pointer-constant "
"-Wno-cast-qual -Wno-missing-noreturn -Wno-missing-field-initializers "
"-Wno-missing-braces -Wno-initializer-overrides "
"-Wno-c99-extensions -Wno-c++98-compat-pedantic -Wno-c++98-compat "
"-Wno-switch-enum -Wno-switch-default "
"-Wno-reserved-identifier -Wno-reserved-macro-identifier "
"-Wno-unsafe-buffer-usage -Wno-writable-strings "
""
"-Wno-double-promotion"));
/* -Wno-unused-variable -Wno-unused-but-set-variable -Wno-unused-parameter */
/* Pre-compiled header */
StringListAppend(&arena, &c_compile_args, StringF(&arena, Lit("-include-pch %F"), FmtStr(pch_c_output.full_path)));
StringListAppend(&arena, &cpp_compile_args, StringF(&arena, Lit("-include-pch %F"), FmtStr(pch_cpp_output.full_path)));
}
/* RTC */
if (RTC) {
if (!CRTLIB) {
Error(Lit("CRTLIB (C runtime library) Must be enabled when compiling with RTC (runtime checks)"));
OS_Exit(1);
}
StringListAppend(&arena, &compile_and_link_args, Lit("-DRTC=1"));
if (MSVC) {
if (!ASAN) {
/* Enable /RTC option (not compatible with ASAN) */
StringListAppend(&arena, &compile_and_link_args, Lit("/RTCcsu"));
}
} else {
/* Enable UBSan */
StringListAppend(&arena, &compile_and_link_args, Lit("-fsanitize=undefined -fsanitize-trap=all"));
//StringListAppend(&compile_and_link_args, "-fsanitize=undefined");
}
}
/* CRTLIB */
if (CRTLIB) {
StringListAppend(&arena, &compile_and_link_args, Lit("-DCRTLIB=1"));
String crt_libs = { 0 };
if (MSVC) {
crt_libs = RTC ?
Lit("msvcrtd.lib ucrtd.lib msvcprtd.lib vcruntimed.lib") :
Lit("msvcrt.lib ucrt.lib msvcprt.lib vcruntime.lib");
} else {
crt_libs = RTC ?
Lit("-lmsvcrtd -lucrtd -lmsvcprtd -lvcruntimed") :
Lit("-lmsvcrt -lucrt -lmsvcprt -lvcruntime");
}
StringListAppend(&arena, &link_args, crt_libs);
} else {
if (MSVC) {
/* TODO */
Error(Lit("TODO\n"));
OS_Exit(1);
} else {
StringListAppend(&arena, &compile_and_link_args, Lit("-mno-stack-arg-probe -fno-builtin"));
}
}
/* Optimization */
if (UNOPTIMIZED) {
StringListAppend(&arena, &compile_and_link_args, Lit("-DUNOPTIMIZED=1"));
StringListAppend(&arena, &compile_and_link_args, MSVC ? Lit("/Od") : Lit("-O0"));
} else {
StringListAppend(&arena, &compile_and_link_args, MSVC ? Lit("/O2 /LTCG") : Lit("-O3 -flto"));
}
/* Debug info */
if (DEBINFO) {
StringListAppend(&arena, &compile_and_link_args, Lit("-DDEBINFO=1"));
StringListAppend(&arena, &compile_and_link_args, MSVC ? Lit("/Zi") : Lit("-g"));
}
/* Address sanitizer */
if (ASAN) {
if (!CRTLIB) {
Error(Lit("CRTLIB (C runtime library) Must be enabled when compiling with asan enabled"));
OS_Exit(1);
}
StringListAppend(&arena, &compile_and_link_args, Lit("-DASAN=1"));
if (MSVC) {
/* TODO: Copy asan libs */
StringListAppend(&arena, &compile_and_link_args, Lit("/fsanitize=address"));
} else {
StringListAppend(&arena, &compile_and_link_args, Lit("-fsanitize=address -shared-libasan"));
}
}
/* Developer mode */
if (DEVELOPER) {
StringListAppend(&arena, &compile_and_link_args, Lit("-DDEVELOPER=1"));
}
/* Profiling */
if (PROFILING) {
if (!CRTLIB) {
Error(Lit("CRTLIB (C runtime library) Must be enabled when compiling with profiling enabled"));
OS_Exit(1);
}
if (MSVC) {
Error(Lit("MSVC not supported with profiling enabled (Profiling relies on Clang attributes)"));
OS_Exit(1);
}
StringListAppend(&arena, &compile_and_link_args, Lit("-DPROFILING=1"));
/* Tracy flags */
StringListAppend(&arena, &compile_and_link_args, Lit("-DTRACY_ENABLE=1"));
StringListAppend(&arena, &compile_and_link_args, Lit("-DTRACY_CALLSTACK=5"));
StringListAppend(&arena, &compile_and_link_args, Lit("-DTRACY_NO_SAMPLING -DTRACY_NO_SYSTEM_TRACING -DTRACY_NO_CALLSTACK"));
/* Disable warnings when compiling tracy client */
warnings = (StringList) { 0 };
}
final_c_compile_args_fmt = StringFromStringLists(&arena, Lit(" "), c_compile_args, compile_args, compile_and_link_args, warnings);
final_cpp_compile_args_fmt = StringFromStringLists(&arena, Lit(" "), cpp_compile_args, compile_args, compile_and_link_args, warnings);
//final_pch_c_compile_args_fmt = StringFromStringLists(&arena, Lit(" "), pch_c_compile_args, compile_args, compile_and_link_args, warnings);
//final_pch_cpp_compile_args_fmt = StringFromStringLists(&arena, Lit(" "), pch_cpp_compile_args, compile_args, compile_and_link_args, warnings);
final_pch_c_compile_args_fmt = StringFromStringLists(&arena, Lit(" "), pch_c_compile_args, compile_args, compile_and_link_args);
final_pch_cpp_compile_args_fmt = StringFromStringLists(&arena, Lit(" "), pch_cpp_compile_args, compile_args, compile_and_link_args);
final_link_args_fmt = StringFromStringLists(&arena, Lit(" "), link_args, compile_and_link_args, warnings);
}
/* ========================== *
* Generate embeddable tar files
* ========================== */
bool embed_in_rc = !!MSVC;
D_Tag res_dir = D_DirTagFromPath(&arena, Lit("res"));
D_Tag shaders_dir = D_DirTagFromPath(&arena, Lit("src/shaders"));
D_Tag inc_file = D_FileTagFromPath(&arena, Lit("src/inc.c"));
RcIncludeList rc_includes = { 0 };
/* Generate shaders tar */
D_Tag shaders_tar = D_FileTagFromPath(&arena, Lit("build/shaders.tar"));
D_AddDependency(shaders_tar, shaders_dir);
if (embed_in_rc) {
RcIncludeListAppend(&arena, &rc_includes, shaders_tar, Lit("RCDATA"));
} else {
D_AddDependency(inc_file, shaders_tar);
}
if (D_IsDirty(shaders_tar)) {
String tar_cmd = StringF(&arena, Lit("cd %F && tar cvf %F ."), FmtStr(shaders_dir.full_path), FmtStr(shaders_tar.full_path));
SH_PrintF(Lit("Running command \"%F\"\n"), FmtStr(tar_cmd));
I32 error = SH_RunCommand(tar_cmd, false);
}
/* Generate res tar */
if (!DEVELOPER) {
D_Tag res_tar = D_FileTagFromPath(&arena, Lit("build/res.tar"));
D_AddDependency(res_tar, res_dir);
if (embed_in_rc) {
RcIncludeListAppend(&arena, &rc_includes, res_tar, Lit("RCDATA"));
} else {
D_AddDependency(inc_file, res_tar);
}
if (D_IsDirty(res_tar)) {
String tar_cmd = StringF(&arena, Lit("cd %F && tar cvf %F ."), FmtStr(res_dir.full_path), FmtStr(res_tar.full_path));
SH_PrintF(Lit("Running command \"%F\"\n"), FmtStr(tar_cmd));
I32 error = SH_RunCommand(tar_cmd, false);
}
}
/* ========================== *
* RC file (windows)
* ========================== */
if (PlatformWindows) {
D_Tag rc_file = D_FileTagFromPath(&arena, Lit("build/rc.rc"));
/* Add icon file to rc list */
{
D_Tag icon_file = D_FileTagFromPath(&arena, Lit("icon.ico"));
RcIncludeListAppend(&arena, &rc_includes, icon_file, Lit("ICON"));
}
/* Add rc dependencies */
for (RcIncludeListNode *rin = rc_includes.first; rin; rin = rin->next) {
D_AddDependency(rc_file, rin->tag);
}
if (D_IsDirty(rc_file)) {
/* Generate rc file */
D_ClearWrite(rc_file, Lit(""));
for (RcIncludeListNode *rin = rc_includes.first; rin; rin = rin->next) {
String name = D_GetName(rin->tag);
String line = StringF(&arena, Lit("%F %F DISCARDABLE %F\n"), FmtStr(name), FmtStr(rin->rc_type), FmtStr(name));
D_AppendWrite(rc_file, line);
}
/* Append rc -> res compile command */
D_Tag rc_res_file = D_FileTagFromPath(&arena, Lit("build/rc.res"));
String rc_compile_cmd = { 0 };
if (MSVC) {
rc_compile_cmd = StringF(&arena, Lit("msdev && rc %F"), FmtStr(rc_file.full_path));
} else {
rc_compile_cmd = StringF(&arena, Lit("msdev && llvm-rc %F"), FmtStr(rc_file.full_path));
}
CompileCommandListAppend(&arena, &compile_command_list, rc_compile_cmd, rc_res_file.full_path);
}
}
/* ========================== *
* Add pch compile commands
* ========================== */
{
/* C */
{
String comp_cmd = StringF(&arena, final_pch_c_compile_args_fmt, FmtStr(pch_input.full_path), FmtStr(pch_c_output.full_path));
CompileCommandListAppend(&arena, &compile_command_list, comp_cmd, (String) { 0 });
}
/* Cpp */
{
String comp_cmd = StringF(&arena, final_pch_cpp_compile_args_fmt, FmtStr(pch_input.full_path), FmtStr(pch_cpp_output.full_path));
CompileCommandListAppend(&arena, &compile_command_list, comp_cmd, (String) { 0 });
}
}
/* ========================== *
* Add src file compile commands
* ========================== */
D_Tag src_dir = D_DirTagFromPath(&arena, Lit("src"));
D_TagList src_files = D_GetDirContents(&arena, src_dir);
for (D_TagListNode *n = src_files.first; n; n = n->next) {
D_Tag file = n->tag;
bool include = !file.is_dir;
if (!include) continue;
String name = D_GetName(file);
String extension = D_GetExtension(file);
bool is_c = StringEqual(extension, Lit("c"));
bool is_cpp = !is_c && StringEqual(extension, Lit("cpp"));
include = (is_c || is_cpp) && D_IsDirty(file);
if (!include) continue;
/* Determine platform specific source files */
{
if (StringBeginsWith(name, Lit("sys_")) ||
StringBeginsWith(name, Lit("renderer_")) ||
StringBeginsWith(name, Lit("playback_")) ||
StringBeginsWith(name, Lit("mp3_")) ||
StringBeginsWith(name, Lit("ttf_"))) {
include = false;
if (PlatformWindows) {
include = StringEqual(name, Lit("sys_win32.c")) ||
StringEqual(name, Lit("renderer_d3d11.c")) ||
StringEqual(name, Lit("playback_wasapi.c")) ||
StringEqual(name, Lit("mp3_mmf.c")) ||
StringEqual(name, Lit("ttf_dwrite.cpp"));
}
}
}
if (!include) continue;
String obj_file_path = { 0 };
{
String name_no_extension = name;
if ((extension.len + 1) <= name.len) {
name_no_extension.len -= extension.len + 1;
}
obj_file_path = StringF(&arena, Lit("build/%F.%F"), FmtStr(name_no_extension), FmtStr(obj_file_extension));
obj_file_path = OS_GetAbsPath(&arena, obj_file_path);
}
String comp_cmd_fmt = is_c ? final_c_compile_args_fmt : final_cpp_compile_args_fmt;
String comp_cmd = StringF(&arena, comp_cmd_fmt, FmtStr(file.full_path), FmtStr(obj_file_path));
CompileCommandListAppend(&arena, &compile_command_list, comp_cmd, obj_file_path);
}
/* ========================== *
* Compile / link
* ========================== */
if (compile_command_list.first) {
StringList link_files = { 0 };
/* Compile */
Size comp_i = 0;
Size comp_count = compile_command_list.count;
for (CompileCommandListNode *n = compile_command_list.first; n; n = n->next) {
++comp_i;
String comp_cmd = n->comp_command;
if (comp_cmd.len > 0 ) {
//SH_PrintF(Lit("[Comp %F/%F] %F\n"), FmtI64(comp_i), FmtI64(comp_count), FmtStr(comp_cmd));
SH_PrintF(Lit("[Comp %F/%F]\n"), FmtI64(comp_i), FmtI64(comp_count));
SH_CommandResult result = SH_RunCommandCaptureOutput(&arena, comp_cmd, false);
if (result.error != 0) {
Error(Lit("Compilation failed"));
OS_Exit(1);
}
}
String link_file_path = n->link_file_path;
if (link_file_path.len > 0) {
StringListAppend(&arena, &link_files, link_file_path);
}
}
/* Link */
{
String link_files_str = StringFromStringList(&arena, Lit(" "), link_files);
String link_cmd = StringF(&arena, final_link_args_fmt, FmtStr(link_files_str), FmtStr(executable.full_path));
//SH_PrintF(Lit("[Link] %F\n"), FmtStr(link_cmd));
SH_Print(Lit("Linking...\n"));
SH_CommandResult result = SH_RunCommandCaptureOutput(&arena, link_cmd, false);
if (result.error != 0) {
Error(Lit("Linking failed"));
OS_Exit(1);
}
D_SetDirty(executable);
}
} else {
/* Nothing to build */
SH_Print(Lit("Nothing to build"));
}
#if 0
#if Rtc
getchar();
#endif
#endif
}