diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 64f7c45a5..a48670180 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,16 +11,16 @@ jobs: include: - suffix: x64-win-dx11 os: windows-latest - build_cmd: bin\build.bat /DWIN32_DX11 + build_cmd: .\build.bat --gl-api=DX11 - suffix: x64-win-gl os: windows-latest - build_cmd: bin\build.bat /DWIN32_OPENGL + build_cmd: .\build.bat --gl-api=OpenGL - suffix: x64-linux os: ubuntu-latest - build_cmd: sudo apt update -y && sudo apt install build-essential libx11-dev libxfixes-dev libglx-dev mesa-common-dev libasound2-dev libfreetype-dev libfontconfig-dev -y && ./bin/build-linux.sh + build_cmd: sudo apt update -y && sudo apt install build-essential libx11-dev libxfixes-dev libglx-dev mesa-common-dev libasound2-dev libfreetype-dev libfontconfig-dev -y && ./build.sh - suffix: x64-mac os: macos-15-intel - build_cmd: ./bin/build-mac.sh + build_cmd: ./build.sh # - suffix: arm64-mac # os: macos-latest # build_cmd: arch -arch x86_64 ./bin/build-mac.sh @@ -29,7 +29,7 @@ jobs: - uses: actions/checkout@v4 - uses: ilammy/msvc-dev-cmd@v1.4.1 # this is a no-op on Linux and macOS - name: Build 4cc-${{ matrix.suffix }} - run: cd code && ${{ matrix.build_cmd }} && cd .. + run: ${{ matrix.build_cmd }} - name: Generate 4cc Artifacts uses: actions/upload-artifact@v4.6.0 with: diff --git a/README.md b/README.md index 6f9cfd072..3884dcc65 100644 --- a/README.md +++ b/README.md @@ -2,24 +2,49 @@ # Building -## Windows +## Build script parameter -> [!NOTE] -> 4coder needs the MSVC compiler and windows SDK from the "Desktop development with C++" component. If you don’t have this already, you can download the installer [here](https://visualstudio.microsoft.com/downloads/). Make sure to at least click on "Desktop development with C++" and tick the boxes for MSVC and the Windows SDK. +``` +Usage: build [options] -1. Setup the MSVC toolchain in your environment, this can be done either with the `code/custom/bin/setup_cl_x64.bat` script or by using the development shell in windows terminal -2. Execute this command from the root of the project (the resulting executable will be under the `build` folder in the root of the repo) +Options: + -m=, --mode= + Sets build mode + Available modes: + - debug + - release + Default: debug + + -gl= --gl-api= + Sets the graphics API to use + Available backends: + - OpenGL + - DX11 (Windows only) + Default: OpenGL + + -s, --symbols + Adds debug symbols. Already set by --mode=debug. + + -o, --optimizations + Adds optimizations. Already set by --mode=release. + + -h, --help + Display this help text and exit + + -v, --verbose + Display build debug information like command currentely executing -```batch -cd code && .\bin\build.bat ``` -In addition to the parameter listed below, you can specify which backend to use by passing one of those parameters to the build scripts: -- `/DWIN32_OPENGL` (default) to use the OpenGL backend. -- `/DWIN32_DX11` to use the Direct3D 11 backend. +## Windows + +> [!NOTE] +> 4coder needs the MSVC compiler and windows SDK from the "Desktop development with C++" component. If you don’t have this already, you can download the installer [here](https://visualstudio.microsoft.com/downloads/). Make sure to at least click on "Desktop development with C++" and tick the boxes for MSVC and the Windows SDK. + +1. Execute this command from the root of the project (the resulting executable will be under the `build` folder in the root of the repo) -```batch -cd code && .\bin\build.bat /DWIN32_DX11 +```cmd +.\build.bat ``` ## Linux @@ -35,7 +60,7 @@ sudo apt install build-essential libx11-dev libxfixes-dev libglx-dev mesa-common 2. Execute this command from the root of the project (the resulting executable will be under the `build` folder in the root of the repo) ```sh -cd code && ./bin/build-linux.sh +./build.sh ``` ## Mac @@ -45,27 +70,11 @@ cd code && ./bin/build-linux.sh 1. Execute this command from the root of the project (the resulting executable will be under the `build` folder in the root of the repo) ```sh -cd code && ./bin/build-mac.sh +./build.sh +# For M1+ Macs +arch -arch x86_64 ./build.sh ``` -### Older Macs, 10.15.7 Catalina - -If you are using an older version of mac, such as 10.15.7 Catalina you need to install the realpath command: - -1. `$ sudo port install coreutils` -2. macports names the `realpath` command `grealpath`, so make a symbolic link in order to use build-mac.sh: - `$ sudo ln -s /opt/local/bin/grealpath /opt/local/bin/realpath` - -## Build script parameter - -The build script accepts a parameter (mutually exclusive): -- `/DDEV_BUILD` (default value) : build without optimizations. - Produces debug symbols. - Defines: `FRED_INTERNAL`, `FRED_SUPER`, `DO_CRAZY_EXPENSIVE_ASSERTS` (on Windows) macros. -- `/DOPT_BUILD` (similar to `build_optimized` script): build with optimizations. - Doesn't produce debug symbols. - Defines `FRED_SUPER` macro. - ## API generators 4coder uses several small programs to generate some headers and source files. Those do not run automatically, you must build them and run them when needed (which shouldn't really happen). diff --git a/build.bat b/build.bat new file mode 100644 index 000000000..2f05fd19c --- /dev/null +++ b/build.bat @@ -0,0 +1,27 @@ +@echo off + +set prev_cwd=%cd% + +rem Make sure we are at the root of the project +cd /D "%~dp0" + +set src_root=%cd%\code +set custom_root=%src_root%\custom +set build_root=%cd%\build + +rem If the user want's 32 bits they need to setup MSVC before calling the script. +call %custom_root%\bin\setup_cl_x64.bat + +set opts=/nologo /FC /Zi /I%src_root% /I%custom_root% + +if not exist "%build_root%" mkdir %build_root% +pushd %build_root% +call cl %opts% %src_root%\4ed_build.cpp /Febuild +popd + +if %ERRORLEVEL% neq 0 goto END + +%build_root%\build.exe %* + +:END +cd /D "%prev_cwd%" \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100755 index 000000000..088424aef --- /dev/null +++ b/build.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +# '/bin/sh' is the most portable way to write a shellscript but doesn't +# support some features that bash does (e.g. using '[[' is not supported +# you can you '['). +# +# To be sure that the script is portable and fully POSIX # compliant use a +# tool like shellcheck to validate it. +# +# https://github.com/koalaman/shellcheck + +# shellcheck disable=SC2048,SC2086 + +set -e + +# Make sure we are at the root of the project +cd "$(realpath "$(dirname "$0")")" + +SRC_ROOT="$PWD/code" +CUSTOM_ROOT="$SRC_ROOT/custom" +BUILD_ROOT="$PWD/build" + +# c++ is the default toolchain's compiler installed on the machine +if [ -z "$CC" ]; then + CC=c++ +fi + +CFLAGS="-std=c++11 -D_GNU_SOURCE -fPIC -fpermissive -Wno-write-strings -Wno-comment" +CFLAGS="$CFLAGS -Wno-null-dereference -Wno-logical-op-parentheses -Wno-switch" +CFLAGS="$CFLAGS -I$SRC_ROOT -I$CUSTOM_ROOT" + +if [ ! -d "$BUILD_ROOT" ]; then + mkdir -p "$BUILD_ROOT" +fi + +eval "$CC $CFLAGS $SRC_ROOT/4ed_build.cpp -g -o $BUILD_ROOT/build" + +"$BUILD_ROOT/build" $* diff --git a/code/4ed_build.cpp b/code/4ed_build.cpp new file mode 100644 index 000000000..723f01888 --- /dev/null +++ b/code/4ed_build.cpp @@ -0,0 +1,606 @@ +/* + * 4coder development build rule. + */ + +#include "4coder_base_types.h" + +#include "4coder_base_types.cpp" +#include "4coder_malloc_allocator.cpp" + +//'prev_error' and 'error_state' come from "4coder_file_moving.h" and indicate +// an error happened with a file moving operation. +#define FTECH_FILE_MOVING_IMPLEMENTATION +#include "4coder_file_moving.h" + +// Utilities + +#define ForEachNode(NAME, LIST) \ + for (auto* NAME = (LIST)->first; NAME != NULL; NAME = NAME->next) + +#define smchi(X, Y) string_match_insensitive(X, Y) + +// Constants + +static char* BUILD_ROOT; +static char* SRC_ROOT; +static char* NON_SRC_ROOT; +static char* CUSTOM_ROOT; +static char* FOREIGN_ROOT; + +// Build Options + +enum { + LIBS = bit_1, + ICON = bit_2, + SHARED_CODE = bit_3, + DEBUG_INFO = bit_4, + OPTIMIZATION = bit_5, + SHIP = bit_6, + VERBOSE = bit_7, +}; + +enum GLAPI { + GLAPI_OPENGL, + GLAPI_DX11, +}; + +enum BuildMode { + MODE_DEBUG = DEBUG_INFO, + MODE_RELEASE = OPTIMIZATION | SHIP, +}; + +struct BuildFlags { + BuildMode type = MODE_DEBUG; + GLAPI backend = GLAPI_OPENGL; + u32 opts = 0; +}; + +#if COMPILER_CL && OS_WINDOWS ///////////////////////////////////////////////////////////////////// + +void build( + Arena *arena, + const char* out_file, + List_String_Const_char files, + List_String_Const_char defines, + List_String_Const_char exports, + List_String_Const_char includes, + BuildFlags flags +) { + Temp_Dir temp = fm_pushdir(BUILD_ROOT); + + Build_Line line; + fm_init_build_line(&line); + + fm_add_to_line(line, "cl"); + + fm_add_to_line(line, "-Fe%s", out_file); + + const char* opts[] = { + "-W4", // Enables all warnings + "-WX", // Raise warnings to errors + "-nologo", // Suppress copyright message + "-FC", // Use full pathnames in diagnostics + "-GR-", // Disable Runtime Type informations + "-EHa-", // Disable C++ EH (w/ SEH exceptions) + "-wd4310", // Disable "cast truncates constant value" + "-wd4100", // Disable "'identifier' : unreferenced formal parameter" + "-wd4201", // Disable "nonstandard extension used : nameless struct/union " + "-wd4505", // Disable "'function' : unreferenced local function has been removed" + "-wd4996", // Disable "deprecation warnings" + "-wd4127", // Disable "conditional expression is constant" + "-wd4510", // Disable "'class': default constructor was implicitly defined as deleted" + "-wd4512", // Disable "'class' : assignment operator could not be generated " + "-wd4610", // Disable "object 'class' can never be instantiated - user-defined constructor required" + "-wd4390", // Disable "';' : empty controlled statement found; is this the intent?" + "-wd4611", // Disable "interaction between 'function' and C++ object destruction is non-portable" + "-wd4189", // Disable "'identifier' : local variable is initialized but not referenced" + }; + + for (int i = 0; i < ArrayCount(opts); ++i) { + fm_add_to_line(line, "%s", opts[i]); + } + + if (flags.type == MODE_DEBUG && !(flags.opts & OPTIMIZATION)) { + fm_add_to_line(line, "-Od"); + } + + if (flags.opts & DEBUG_INFO) { + fm_add_to_line(line, "-Zi"); + } + + if (flags.opts & OPTIMIZATION) { + fm_add_to_line(line, "-O2"); + } + + if (flags.opts & SHARED_CODE) { + fm_add_to_line(line, "-LD"); + } + + ForEachNode(iter, &includes) { + fm_add_to_line(line, "-I%.*s", string_expand(iter->string)); + } + + ForEachNode(iter, &defines) { + fm_add_to_line(line, "-D%.*s", string_expand(iter->string)); + } + + ForEachNode(iter, &files) { + fm_add_to_line(line, "\"%.*s\"", string_expand(iter->string)); + } + + if (flags.opts & ICON) { + fm_add_to_line(line, "..\\non-source\\res\\icon.res"); + } + + if (flags.opts & LIBS) { + fm_add_to_line(line, "%s", "user32.lib winmm.lib gdi32.lib comdlg32.lib userenv.lib"); + + if (ARCH_X64) { + fm_add_to_line(line, "%s", fm_str(arena, FOREIGN_ROOT, "\\x64\\freetype.lib")); + } else if (ARCH_X86) { + fm_add_to_line(line, "%s", fm_str(arena, FOREIGN_ROOT, "\\x86\\freetype.lib")); + } else { + InvalidPath; + } + + switch (flags.backend) { + case GLAPI_OPENGL: { + fm_add_to_line(line, "OpenGL32.lib"); + } break; + case GLAPI_DX11: { + fm_add_to_line(line, "d3d11.lib dxgi.lib d3dcompiler.lib"); + } break; + default: { + InvalidPath; + }break; + } + } + + + fm_add_to_line(line, "-link -INCREMENTAL:NO -RELEASE -PDBALTPATH:%%_PDB%%"); + + if (ARCH_X64) { + fm_add_to_line(line, "-MACHINE:X64"); + } else if (ARCH_X86) { + fm_add_to_line(line, "-MACHINE:X86"); + } else { + InvalidPath; + } + + if (flags.opts & DEBUG_INFO){ + fm_add_to_line(line, "-DEBUG"); + } + + if (flags.opts & SHARED_CODE) { + fm_add_to_line(line, "-OPT:REF"); + ForEachNode(iter, &exports) { + fm_add_to_line(line, "-EXPORT:%.*s", string_expand(iter->string)); + } + } else { + fm_add_to_line(line, "-NODEFAULTLIB:library"); + } + + fm_finish_build_line(&line); + + if (flags.opts & VERBOSE) { + printf("%s\n", line.build_options); + } + + systemf("%s", line.build_options); + fm_popdir(temp); +} + +#elif COMPILER_GCC && OS_LINUX //////////////////////////////////////////////////////////////////// + +void build( + Arena *arena, + const char* out_file, + List_String_Const_char files, + List_String_Const_char defines, + List_String_Const_char exports, + List_String_Const_char includes, + BuildFlags flags +) { + Temp_Dir temp = fm_pushdir(BUILD_ROOT); + + Build_Line line; + fm_init_build_line(&line); + + fm_add_to_line(line, "g++"); + + fm_add_to_line(line, "-o %s", out_file); + + const char* opts[] = { + "-D_GNU_SOURCE", // Enable GNU extentions + "-fPIC", // Enable Position Independent Code + "-fno-threadsafe-statics -pthread", // Don't use C++ thread safety routines + "-Wno-write-strings", // Disable "warning: deprecated conversion from string constant to 'char*'" + "-Wno-unused-result", // Disable "warning: ignoring return value of 'function_name'" + "-std=c++11", + }; + + for (int i = 0; i < ArrayCount(opts); ++i) { + fm_add_to_line(line, "%s", opts[i]); + } + + if (flags.type == MODE_DEBUG && !(flags.opts & OPTIMIZATION)) { + fm_add_to_line(line, "-O0"); + } + + if (flags.opts & DEBUG_INFO) { + fm_add_to_line(line, "-g"); + } + + if (flags.opts & OPTIMIZATION) { + fm_add_to_line(line, "-O3"); + } + + if (flags.opts & SHARED_CODE) { + fm_add_to_line(line, "-shared"); + } + + ForEachNode(iter, &defines) { + fm_add_to_line(line, "-D%.*s", string_expand(iter->string)); + } + + ForEachNode(iter, &includes) { + fm_add_to_line(line, "-I%.*s", string_expand(iter->string)); + } + + ForEachNode(iter, &files) { + fm_add_to_line(line, "'%.*s'", string_expand(iter->string)); + } + + if (flags.opts & LIBS) { + fm_add_to_line(line, "-lX11 -lpthread -lm -lrt -lGL -ldl -lXfixes -lXi -lfreetype -lfontconfig"); + } + + fm_finish_build_line(&line); + + if (flags.opts & VERBOSE) { + printf("%s\n", line.build_options); + } + + systemf("%s", line.build_options); + fm_popdir(temp); +} + +#elif COMPILER_CLANG && OS_MAC //////////////////////////////////////////////////////////////////// + +void build( + Arena *arena, + const char* out_file, + List_String_Const_char files, + List_String_Const_char defines, + List_String_Const_char exports, + List_String_Const_char includes, + BuildFlags flags +) { + Temp_Dir temp = fm_pushdir(BUILD_ROOT); + + Build_Line line; + fm_init_build_line(&line); + + fm_add_to_line(line, "clang++"); + + fm_add_to_line(line, "-o %s", out_file); + + const char* opts[] = { + "-Wno-write-strings", // Disable "warning: deprecated conversion from string constant to 'char*'" + "-Wno-deprecated-declarations", // Disable deprecation warnings + "-Wno-switch", // Disable "enumeration value B not handled in switch" + "-Wno-null-dereference", // Disable "warning: binding dereferenced null pointer to reference has undefined behavior" + "-Wno-unused-result", // Disable "warning: ignoring return value of 'function_name'" + "-Wno-missing-declarations", // Disable "warning: declaration does not declare anything" + "-Wno-nullability-completeness", // Disable "warning: pointer is missing a nullability type specifier (_Nonnull, _Nullable, or _Null_unspecified)" + "-Wno-tautological-compare", + "-std=c++11 ", + }; + + for (int i = 0; i < ArrayCount(opts); ++i) { + fm_add_to_line(line, "%s", opts[i]); + } + + if (flags.type == MODE_DEBUG && !(flags.opts & OPTIMIZATION)) { + fm_add_to_line(line, "-O0"); + } + + if (flags.opts & DEBUG_INFO) { + fm_add_to_line(line, "-g"); + } + + if (flags.opts & OPTIMIZATION) { + fm_add_to_line(line, "-O3"); + } + + if (flags.opts & SHARED_CODE) { + fm_add_to_line(line, "-shared"); + } + + ForEachNode(iter, &includes) { + fm_add_to_line(line, "-I%.*s", string_expand(iter->string)); + } + + ForEachNode(iter, &defines) { + fm_add_to_line(line, "-D%.*s", string_expand(iter->string)); + } + + ForEachNode(iter, &files) { + fm_add_to_line(line, "'%.*s'", string_expand(iter->string)); + } + + if (flags.opts & LIBS) { + fm_add_to_line(line, "-framework Cocoa -framework QuartzCore " + "-framework CoreServices -framework OpenGL " + "-framework IOKit -framework Metal " + "-framework MetalKit"); + + if (ARCH_X64) { + fm_add_to_line(line, "%s", fm_str(arena, FOREIGN_ROOT, "/x64/libfreetype-mac.a")); + } else if (ARCH_X86) { + fm_add_to_line(line, "%s", fm_str(arena, FOREIGN_ROOT, "/x86/libfreetype-mac.a")); + } else { + InvalidPath; + } + } + + fm_finish_build_line(&line); + + if (flags.opts & VERBOSE) { + printf("%s\n", line.build_options); + } + + systemf("%s", line.build_options); + fm_popdir(temp); +} + +#else +# error build function not defined for this compiler +#endif //////////////////////////////////////////////////////////////////////////////////////////// + +function void +dispatch_build(Arena* arena, BuildFlags flags) { + // Build Super + { + Temp_Dir dir = fm_pushdir(BUILD_ROOT); + + const char* build_script = fm_str(arena, CUSTOM_ROOT, "/bin/buildsuper_", ARCH_NAME, "-" OS_NAME, BAT); + char* default_custom_target = fm_str(arena, CUSTOM_ROOT, "/4coder_default_bindings.cpp"); + char* build_cmd = fm_str(arena, "\"", build_script, "\" \"", default_custom_target, "\""); + + if (OS_WINDOWS) { + build_cmd = fm_str(arena, "call ", build_cmd); + } + + if (flags.type == MODE_RELEASE) { + build_cmd = fm_str(arena, build_cmd, " release"); + } + + if (flags.opts & VERBOSE) { + printf("%s\n", build_cmd); + } + + systemf("%s", build_cmd); + + fm_popdir(dir); + + if (prev_error != 0) { + return; + } + } + + // Setup prequesites for build + + List_String_Const_char includes = {}; + string_list_push(arena, &includes, SCchar(SRC_ROOT)); + string_list_push(arena, &includes, SCchar(CUSTOM_ROOT)); + string_list_push(arena, &includes, SCchar(fm_str(arena, FOREIGN_ROOT, "/freetype2"))); + + if (OS_MAC || OS_LINUX) { + string_list_push(arena, &includes, SCchar("platform_unix")); + } + + List_String_Const_char defines = {}; + switch (flags.type) { + case MODE_DEBUG: { + string_list_push(arena, &defines, SCchar("FRED_INTERNAL")); + } break; + case MODE_RELEASE: { + string_list_push(arena, &defines, SCchar("SHIP_MODE")); + string_list_push(arena, &defines, SCchar("NDEBUG")); + } break; + default: { + InvalidPath; + } break; + } + + if (OS_WINDOWS) { + switch (flags.backend) { + case GLAPI_OPENGL: { + string_list_push(arena, &defines, SCchar("WIN32_OPENGL")); + } break; + case GLAPI_DX11: { + string_list_push(arena, &defines, SCchar("WIN32_DX11")); + } break; + default: { + InvalidPath; + } break; + } + } + + // Build 4ed_app + { + List_String_Const_char files = {}; + string_list_push(arena, &files, SCchar(fm_str(arena, SRC_ROOT, "/4ed_app_target.cpp"))); + + List_String_Const_char exports = {}; + string_list_push(arena, &exports, SCchar("app_get_functions")); + + BuildFlags opts = flags; + opts.opts = flags.type | flags.opts | SHARED_CODE; + build(arena, "4ed_app" DLL, files, defines, exports, includes, opts); + + if (prev_error != 0) { + return; + } + } + + // Build Platform Layer + { + List_String_Const_char files = {}; + if (OS_WINDOWS) { + string_list_push(arena, &files, SCchar(fm_str(arena, SRC_ROOT, "/platform_win32/win32_4ed.cpp"))); + } else if (OS_LINUX) { + string_list_push(arena, &files, SCchar(fm_str(arena, SRC_ROOT, "/platform_linux/linux_4ed.cpp"))); + } else if (OS_MAC) { + string_list_push(arena, &files, SCchar(fm_str(arena, SRC_ROOT, "/platform_mac/mac_4ed.mm"))); + } + + BuildFlags opts = flags; + opts.opts = flags.type | flags.opts | LIBS | ICON; + build(arena, "4ed", files, defines, {}, includes, opts); + + if (prev_error != 0) { + return; + } + } + + // Copy distribution files into build dir + { + char *files[] = { + fm_str(arena, NON_SRC_ROOT, "/dist_files"), + fm_str(arena, SRC_ROOT, "/ship_files"), + }; + + for (int i = 0; i < ArrayCount(files); i += 1) { + fm_copy_all(files[i], BUILD_ROOT); + } + + char* custom_dst = fm_str(arena, BUILD_ROOT, "/custom"); + fm_make_folder_if_missing(arena, custom_dst); + fm_copy_all(CUSTOM_ROOT, custom_dst); + } +} + +void usage(FILE* fd) { + fprintf(fd, R"(Usage: build [options] + +Options: + -m=, --mode= + Sets build mode + Available modes: + - debug + - release + Default: debug + + -gl= --gl-api= + Sets the graphics API to use + Available backends: + - OpenGL + - DX11 (Windows only) + Default: OpenGL + + -s, --symbols + Adds debug symbols. Already set by --mode=debug. + + -o, --optimizations + Adds optimizations. Already set by --mode=release. + + -h, --help + Display this help text and exit + + -v, --verbose + Display build debug information like command currentely executing + )"); +} + +String_Const_char get_flag_value(String_Const_char flag) { + for (int i = 0; i < flag.size; ++i) { + if (flag.str[i] == '=') { + return SCchar(flag.str + i + 1, flag.size - i - 1); + } + } + return SCchar(""); +} + +BuildFlags parse_flags(int argc, const char** argv) { + // Default flags + BuildFlags flags = {}; + + for (int i = 1; i < argc; ++i) { + String_Const_char arg = SCchar((char*)argv[i]); + + if (smchi(arg, SCchar("-h")) || smchi(arg, SCchar("--help"))) { + usage(stdout); + exit(0); + } else if (smchi(arg, SCchar("-v")) || smchi(arg, SCchar("--verbose"))) { + flags.opts |= VERBOSE; + } else if (smchi(arg, SCchar("-s")) || smchi(arg, SCchar("--symbols"))) { + flags.opts |= DEBUG_INFO; + } else if (smchi(arg, SCchar("-o")) || smchi(arg, SCchar("--optimiziations"))) { + flags.opts |= OPTIMIZATION; + } else if (string_has_prefix(arg, SCchar("-m=")) || string_has_prefix(arg, SCchar("--mode="))) { + String_Const_char param = get_flag_value(arg); + if (smchi(param, SCchar("release"))) { + flags.type = MODE_RELEASE; + } else if (smchi(param, SCchar("debug"))) { + flags.type = MODE_DEBUG; + } else { + fprintf(stderr, "build: invalid mode '%.*s'\n", string_expand(param)); + usage(stderr); + exit(1); + } + } else if (string_has_prefix(arg, SCchar("--gl-api=")) || string_has_prefix(arg, SCchar("-gl="))) { + String_Const_char param = get_flag_value(arg); + + if (smchi(param, SCchar("DX11")) && OS_WINDOWS) { + flags.backend = GLAPI_DX11; + } else if (smchi(param, SCchar("OpenGL"))) { + flags.backend = GLAPI_OPENGL; + } else { + fprintf(stderr, "build: invalid backend '%.*s'\n", string_expand(param)); + usage(stderr); + exit(1); + } + } else { + fprintf(stderr, "build: unrecognised option '%.*s'\n", string_expand(arg)); + usage(stderr); + exit(1); + } + } + + return flags; +} + +bool set_common_dirs(Arena* arena) { + char cwd[256]; + int cwd_size = fm_get_current_directory(cwd, sizeof(cwd)); + if (cwd_size >= sizeof(cwd)) { + fprintf(stderr, "CWD length exceed %zu characters. Aborting.", sizeof(cwd)); + return false; + } + + BUILD_ROOT = fm_str(arena, cwd, "/build"); + SRC_ROOT = fm_str(arena, cwd, "/code"); + NON_SRC_ROOT = fm_str(arena, cwd, "/non-source"); + CUSTOM_ROOT = fm_str(arena, cwd, "/code/custom"); + FOREIGN_ROOT = fm_str(arena, cwd, "/non-source/foreign"); + + return true; +} + +int main(int argc, const char **argv){ + Arena arena = fm_init_system(DetailLevel_FileOperations); + + BuildFlags flags = parse_flags(argc, argv); + + if (!set_common_dirs(&arena)) { + return 1; + } + + dispatch_build(&arena, flags); + + return error_state; +} + +// BOTTOM \ No newline at end of file diff --git a/code/bin/4ed_build.cpp b/code/bin/4ed_build.cpp deleted file mode 100644 index 72635e00b..000000000 --- a/code/bin/4ed_build.cpp +++ /dev/null @@ -1,611 +0,0 @@ -/* - * Mr. 4th Dimention - Allen Webster - * - * ??.??.???? - * - * 4coder development build rule. - * - */ - -// TOP - -//#define FM_PRINT_COMMANDS - -#include "4coder_base_types.h" -#include "4coder_version.h" - -#include "4coder_base_types.cpp" -#include "4coder_malloc_allocator.cpp" - -#define FTECH_FILE_MOVING_IMPLEMENTATION -#include "4coder_file_moving.h" - - -// -// OS and compiler index -// - -typedef u32 Platform_Code; -enum{ - Platform_Windows, - Platform_Linux, - Platform_Mac, - // - Platform_COUNT, - Platform_None = Platform_COUNT, -}; - -char *platform_names[] = { - "win", - "linux", - "mac", -}; - -typedef u32 Compiler_Code; -enum{ - Compiler_CL, - Compiler_GCC, - Compiler_Clang, - // - Compiler_COUNT, - Compiler_None = Compiler_COUNT, -}; - -char *compiler_names[] = { - "cl", - "gcc", - "clang", -}; - -typedef u32 Arch_Code; -enum{ - Arch_X64, - Arch_X86, - Arch_arm64, - - // - Arch_COUNT, - Arch_None = Arch_COUNT, -}; - -char *arch_names[] = { - "x64", - "x86", - "arm64", -}; - -#if OS_WINDOWS -# define This_OS Platform_Windows -#elif OS_LINUX -# define This_OS Platform_Linux -#elif OS_MAC -# define This_OS Platform_Mac -#else -# error This platform is not enumerated. -#endif - -#if COMPILER_CL -# define This_Compiler Compiler_CL -#elif COMPILER_GCC -# define This_Compiler Compiler_GCC -#elif COMPILER_CLANG -# define This_Compiler Compiler_Clang -#else -# error This compilers is not enumerated. -#endif - -// -// Universal directories -// - -#define BUILD_DIR ".." SLASH "build" -#define SITE_DIR ".." SLASH "site" - -#define FOREIGN ".." SLASH "non-source" SLASH "foreign" - -char *includes[] = { "custom", FOREIGN SLASH "freetype2", 0, }; - -// -// Platform layer file tables -// - -char *windows_platform_layer[] = { "platform_win32/win32_4ed.cpp", 0 }; -char *linux_platform_layer[] = { "platform_linux/linux_4ed.cpp", 0 }; -char *mac_platform_layer[] = { "platform_mac/mac_4ed.mm", 0 }; - -char **platform_layers[Platform_COUNT] = { - windows_platform_layer, - linux_platform_layer , - mac_platform_layer , -}; - -char *windows_cl_platform_inc[] = { "platform_all", 0 }; -char *linux_gcc_platform_inc[] = { "platform_all", "platform_unix", 0 }; - -char *mac_clang_platform_inc[] = { "platform_all", "platform_unix", 0 }; - -char **platform_includes[Platform_COUNT][Compiler_COUNT] = { - {windows_cl_platform_inc, 0 , 0}, - {0 , linux_gcc_platform_inc, 0}, - {0 , 0 , mac_clang_platform_inc}, -}; - -char *default_custom_target = ".." SLASH "code" SLASH "custom" SLASH "4coder_default_bindings.cpp"; - -// NOTE(allen): Build flags - -enum{ - OPTS = 0x1, - LIBS = 0x2, - ICON = 0x4, - SHARED_CODE = 0x8, - DEBUG_INFO = 0x10, - OPTIMIZATION = 0x20, - SUPER = 0x40, - INTERNAL = 0x80, - SHIP = 0x100, - OPENGL = 0x200, - DX11 = 0x400, -}; - -internal char** -get_defines_from_flags(Arena *arena, u32 flags){ - char **result = 0; - if (HasFlag(flags, SHIP)){ - result = fm_list(arena, fm_list_one_item(arena, "SHIP_MODE"), result); - } - if (HasFlag(flags, INTERNAL)){ - result = fm_list(arena, fm_list_one_item(arena, "FRED_INTERNAL"), result); - } - if (HasFlag(flags, SUPER)){ - result = fm_list(arena, fm_list_one_item(arena, "FRED_SUPER"), result); - } - if (HasFlag(flags, OPENGL)) { - result = fm_list(arena, fm_list_one_item(arena, "WIN32_OPENGL"), result); - } else if (HasFlag(flags, DX11)) { - result = fm_list(arena, fm_list_one_item(arena, "WIN32_DX11"), result); - } - return(result); -} - -// -// build implementation: cl -// - -#if COMPILER_CL - -#define CL_OPTS \ -"-W4 -wd4310 -wd4100 -wd4201 -wd4505 -wd4996 " \ -"-wd4127 -wd4510 -wd4512 -wd4610 -wd4390 " \ -"-wd4611 -wd4189 -WX -GR- -EHa- -nologo -FC" - -#define CL_LIBS_COMMON \ -"user32.lib winmm.lib gdi32.lib comdlg32.lib userenv.lib " - -#if defined( WIN32_DX11 ) -#define CL_LIBS_BACKEND " d3d11.lib dxgi.lib d3dcompiler.lib" -#else -#define CL_LIBS_BACKEND " OpenGL32.lib" -#endif - -#define CL_LIBS_X64 CL_LIBS_COMMON FOREIGN "\\x64\\freetype.lib" CL_LIBS_BACKEND -#define CL_LIBS_X86 CL_LIBS_COMMON FOREIGN "\\x86\\freetype.lib" CL_LIBS_BACKEND - -#define CL_ICON "..\\non-source\\res\\icon.res" - -internal void -build(Arena *arena, u32 flags, u32 arch, char *code_path, char **code_files, char *out_path, char *out_file, char **defines, char **exports, char **inc_folders){ - Temp_Dir temp = fm_pushdir(out_path); - - Build_Line line; - fm_init_build_line(&line); - - if (arch == Arch_X86){ - fm_add_to_line(line, "%s\\custom\\bin\\setup_cl_x86.bat &", code_path); - } - - fm_add_to_line(line, "cl"); - - if (flags & OPTS){ - fm_add_to_line(line, CL_OPTS); - } - - switch (arch){ - case Arch_X64: fm_add_to_line(line, "-DFTECH_64_BIT"); break; - case Arch_X86: fm_add_to_line(line, "-DFTECH_32_BIT"); break; - default: InvalidPath; - } - - fm_add_to_line(line, "-I%s", code_path); - if (inc_folders != 0){ - for (u32 i = 0; inc_folders[i] != 0; ++i){ - char *str = fm_str(arena, code_path, "/", inc_folders[i]); - fm_add_to_line(line, "-I%s", str); - } - } - - if (flags & LIBS){ - switch (arch){ - case Arch_X64: fm_add_to_line(line, CL_LIBS_X64); break; - case Arch_X86: fm_add_to_line(line, CL_LIBS_X86); break; - default: InvalidPath; - } - } - - if (flags & ICON){ - fm_add_to_line(line, CL_ICON); - } - - if (flags & DEBUG_INFO){ - fm_add_to_line(line, "-Zi"); - fm_add_to_line(line, "-DDO_CRAZY_EXPENSIVE_ASSERTS"); - } - - if (flags & OPTIMIZATION){ - fm_add_to_line(line, "-O2"); - } - - if (flags & SHARED_CODE){ - fm_add_to_line(line, "-LD"); - } - - if (defines != 0){ - for (u32 i = 0; defines[i] != 0; ++i){ - char *define_flag = fm_str(arena, "-D", defines[i]); - fm_add_to_line(line, "%s", define_flag); - } - } - - for (u32 i = 0; code_files[i]; ++i){ - fm_add_to_line(line, "\"%s\\%s\"", code_path, code_files[i]); - } - - fm_add_to_line(line, "-Fe%s", out_file); - - fm_add_to_line(line, "-link -INCREMENTAL:NO -RELEASE -PDBALTPATH:%%_PDB%%"); - switch (arch){ - case Arch_X64: fm_add_to_line(line, "-MACHINE:X64"); break; - case Arch_X86: fm_add_to_line(line, "-MACHINE:X86"); break; - default: InvalidPath; - } - - if (flags & DEBUG_INFO){ - fm_add_to_line(line, "-DEBUG"); - } - - if (flags & SHARED_CODE){ - Assert(exports != 0); - fm_add_to_line(line, "-OPT:REF"); - for (u32 i = 0; exports[i] != 0; ++i){ - char *str = fm_str(arena, "-EXPORT:", exports[i]); - fm_add_to_line(line, "%s", str); - } - } - else{ - fm_add_to_line(line, "-NODEFAULTLIB:library"); - } - - fm_finish_build_line(&line); - - //printf("%s\n", line.build_options); - systemf("%s", line.build_options); - fm_popdir(temp); - - fflush(stdout); -} - -// -// build implementation: gcc -// - -#elif COMPILER_GCC - -#if OS_LINUX - -# define GCC_OPTS \ -"-Wno-write-strings " \ -"-D_GNU_SOURCE -fPIC " \ -"-fno-threadsafe-statics -pthread " \ -"-Wno-unused-result " \ -"-std=c++11" - -# define GCC_LIBS_COMMON \ -"-lX11 -lpthread -lm -lrt " \ -"-lGL -ldl -lXfixes -lfreetype -lfontconfig" - -# define GCC_LIBS_X64 GCC_LIBS_COMMON -# define GCC_LIBS_X86 GCC_LIBS_COMMON - -#else -# error gcc options not set for this platform -#endif - -internal void -build(Arena *arena, u32 flags, u32 arch, char *code_path, char **code_files, char *out_path, char *out_file, char **defines, char **exports, char **inc_folders){ - Build_Line line; - fm_init_build_line(&line); - - switch (arch){ - case Arch_X64: - fm_add_to_line(line, "-m64"); - fm_add_to_line(line, "-DFTECH_64_BIT"); break; - - case Arch_X86: - fm_add_to_line(line, "-m32"); - fm_add_to_line(line, "-DFTECH_32_BIT"); break; - - default: InvalidPath; - } - - if (flags & OPTS){ - fm_add_to_line(line, GCC_OPTS); - } - - fm_add_to_line(line, "-I%s", code_path); - if (inc_folders != 0){ - for (u32 i = 0; inc_folders[i] != 0; ++i){ - char *str = fm_str(arena, code_path, "/", inc_folders[i]); - fm_add_to_line(line, "-I%s", str); - } - } - - if (flags & DEBUG_INFO){ - fm_add_to_line(line, "-g -O0"); - } - - if (flags & OPTIMIZATION){ - fm_add_to_line(line, "-O3"); - } - - if (flags & SHARED_CODE){ - fm_add_to_line(line, "-shared"); - } - - if (defines != 0){ - for (u32 i = 0; defines[i]; ++i){ - char *define_flag = fm_str(arena, "-D", defines[i]); - fm_add_to_line(line, "%s", define_flag); - } - } - - fm_add_to_line(line, "-I\"%s\"", code_path); - for (u32 i = 0; code_files[i] != 0; ++i){ - fm_add_to_line(line, "\"%s/%s\"", code_path, code_files[i]); - } - - if (flags & LIBS){ - if (arch == Arch_X64){ - fm_add_to_line(line, GCC_LIBS_X64); - } - else if (arch == Arch_X86) - { - fm_add_to_line(line, GCC_LIBS_X86); - } - } - - fm_finish_build_line(&line); - - Temp_Dir temp = fm_pushdir(out_path); - systemf("g++ %s -o %s", line.build_options, out_file); - fm_popdir(temp); -} - -#elif COMPILER_CLANG - -#if OS_MAC - -# define CLANG_OPTS \ -"-Wno-write-strings -Wno-deprecated-declarations " \ -"-Wno-comment -Wno-switch -Wno-null-dereference " \ -"-Wno-tautological-compare -Wno-unused-result " \ -"-Wno-missing-declarations -Wno-nullability-completeness " \ -"-std=c++11 " - -# define CLANG_LIBS_COMMON \ -"-framework Cocoa -framework QuartzCore " \ -"-framework CoreServices " \ -"-framework OpenGL -framework IOKit -framework Metal -framework MetalKit " - -# define CLANG_LIBS_X64 CLANG_LIBS_COMMON \ -FOREIGN "/x64/libfreetype-mac.a" - -# define CLANG_LIBS_X86 CLANG_LIBS_COMMON \ -FOREIGN "/x86/libfreetype-mac.a" - -#else -# error clang options not set for this platform -#endif - -internal void -build(Arena *arena, u32 flags, u32 arch, char *code_path, char **code_files, char *out_path, char *out_file, char **defines, char **exports, char **inc_folders){ - Build_Line line; - fm_init_build_line(&line); - - switch (arch){ - case Arch_X64: - fm_add_to_line(line, "-m64"); - fm_add_to_line(line, "-DFTECH_64_BIT"); break; - - case Arch_X86: - fm_add_to_line(line, "-m32"); - fm_add_to_line(line, "-DFTECH_32_BIT"); break; - - default: InvalidPath; - } - - if (flags & OPTS){ - fm_add_to_line(line, CLANG_OPTS); - } - - fm_add_to_line(line, "-I%s", code_path); - if (inc_folders != 0){ - for (u32 i = 0; inc_folders[i] != 0; ++i){ - char *str = fm_str(arena, code_path, "/", inc_folders[i]); - fm_add_to_line(line, "-I%s", str); - } - } - - if (flags & DEBUG_INFO){ - fm_add_to_line(line, "-g -O0"); - } - - if (flags & OPTIMIZATION){ - fm_add_to_line(line, "-O3"); - } - - if (flags & SHARED_CODE){ - fm_add_to_line(line, "-shared"); - } - - if (defines != 0){ - for (u32 i = 0; defines[i]; ++i){ - char *define_flag = fm_str(arena, "-D", defines[i]); - fm_add_to_line(line, "%s", define_flag); - } - } - - fm_add_to_line(line, "-I\"%s\"", code_path); - for (u32 i = 0; code_files[i] != 0; ++i){ - fm_add_to_line(line, "\"%s/%s\"", code_path, code_files[i]); - } - - if (flags & LIBS){ - if (arch == Arch_X64){ - fm_add_to_line(line, CLANG_LIBS_X64); - } - else if (arch == Arch_X86) - { - fm_add_to_line(line, CLANG_LIBS_X86); - } - } - - fm_finish_build_line(&line); - - Temp_Dir temp = fm_pushdir(out_path); - - // systemf("clang++ %s -E -o %s", line.build_options, "4ed.i"); - systemf("clang++ %s -o %s", line.build_options, out_file); - fm_popdir(temp); -} - -#else -# error build function not defined for this compiler -#endif - -internal void -build(Arena *arena, u32 flags, u32 arch, char *code_path, char *code_file, char *out_path, char *out_file, char **defines, char **exports, char **inc_folders){ - char **code_files = fm_list_one_item(arena, code_file); - build(arena, flags, arch, code_path, code_files, out_path, out_file, defines, exports, inc_folders); -} - -function void -dispatch_build(Arena *arena, u32 arch, char *cwd, u32 flags, char** dist_files, i32 dist_file_count){ - Temp_Dir temp = fm_pushdir(fm_str(arena, BUILD_DIR)); - - char *build_script_postfix = ""; - switch (This_OS){ - case Platform_Windows: - { - build_script_postfix = "-win"; - }break; - case Platform_Linux: - { - build_script_postfix = "-linux"; - }break; - case Platform_Mac: - { - build_script_postfix = "-mac"; - }break; - } - char *build_script = fm_str(arena, "custom/bin/buildsuper_", arch_names[arch], build_script_postfix, BAT); - - char *build_command = fm_str(arena, "\"", cwd, SLASH, build_script, "\" \"", default_custom_target, "\""); - if (This_OS == Platform_Windows){ - build_command = fm_str(arena, "call ", build_command); - } - - if (flags & OPTIMIZATION) { - build_command = fm_str(arena, build_command, " release"); - } - - systemf("%s", build_command); - - fm_popdir(temp); - fflush(stdout); - - { - char *file = fm_str(arena, "4ed_app_target.cpp"); - char **exports = fm_list_one_item(arena, "app_get_functions"); - build(arena, OPTS | SHARED_CODE | flags, arch, cwd, file, BUILD_DIR, "4ed_app" DLL, get_defines_from_flags(arena, flags), exports, includes); - } - - { - char **inc = (char**)fm_list(arena, includes, platform_includes[This_OS][This_Compiler]); - build(arena, OPTS | LIBS | ICON | flags, arch, cwd, platform_layers[This_OS], BUILD_DIR, "4ed", get_defines_from_flags(arena, flags), 0, inc); - } - - fflush(stdout); - - for (i32 j = 0; j < dist_file_count; j += 1){ - fm_copy_all(dist_files[j], BUILD_DIR); - } - - char *custom_src_dir = fm_str(arena, cwd, SLASH, "custom"); - char *custom_dst_dir = fm_str(arena, BUILD_DIR, SLASH, "custom"); - fm_make_folder_if_missing(arena, custom_dst_dir); - fm_copy_all(custom_src_dir, custom_dst_dir); -} - -int main(int argc, char **argv){ - Arena arena = fm_init_system(DetailLevel_FileOperations); - - char cwd[256]; - i32 n = fm_get_current_directory(cwd, sizeof(cwd)); - Assert(n < sizeof(cwd)); - - u32 flags = 0; - -#if ARCH_X86 - u32 arch = Arch_X86; -#elif ARCH_X64 - u32 arch = Arch_X64; -#elif ARCH_ARM64 - u32 arch = Arch_arm64; -#endif - -#if defined(DEV_BUILD) - flags |= DEBUG_INFO | INTERNAL; -#endif - -#if defined(OPT_BUILD) - flags |= OPTIMIZATION | SHIP; -#endif - -#if OS_WINDOWS - #if defined(WIN32_DX11) - flags |= DX11; - #else - flags |= OPENGL; - #endif -#endif - - // NOTE(allen): meta - char *dist_files[] = { - fm_str(&arena, "../non-source/dist_files"), - fm_str(&arena, "ship_files"), - }; - - printf("cwd: %s\n", cwd); - printf("BUILD: 4coder\n"); - printf(" arch: %s\n", arch_names[arch]); - printf(" build dir: %s\n", BUILD_DIR); - fflush(stdout); - - Temp_Memory temp = begin_temp(&arena); - dispatch_build(&arena, arch, cwd, flags, dist_files, ArrayCount(dist_files)); - end_temp(temp); - - return(error_state); -} - -// BOTTOM diff --git a/code/bin/build-linux.sh b/code/bin/build-linux.sh deleted file mode 100755 index bce5f56f3..000000000 --- a/code/bin/build-linux.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -# If any command errors, stop the script -set -e - -# Set up directories - -ME="$(readlink -f "$0")" -LOCATION="$(dirname "$ME")" -SRC_ROOT="$(dirname "$LOCATION")" -PROJECT_ROOT="$(dirname "$SRC_ROOT")" -if [ ! -d "$PROJECT_ROOT/build" ]; then -mkdir "$PROJECT_ROOT/build" -fi -BUILD_ROOT="$PROJECT_ROOT/build" -BIN_ROOT="$SRC_ROOT/bin" -CUSTOM_ROOT="$SRC_ROOT/custom" -CUSTOM_BIN="$CUSTOM_ROOT/bin" - -# Get the build mode -BUILD_MODE="$1" -if [ -z "$BUILD_MODE" ]; then - BUILD_MODE="-DDEV_BUILD" -fi - -WARNINGS="-Wno-write-strings -Wno-comment" - -FLAGS="-D_GNU_SOURCE -fPIC -fpermissive $BUILD_MODE" -INCLUDES="-I$SRC_ROOT -I$CUSTOM_ROOT" - -# Execute -g++ $WARNINGS $FLAGS $INCLUDES "$BIN_ROOT/4ed_build.cpp" -g -o "$BUILD_ROOT/build" -pushd "$SRC_ROOT" -"$BUILD_ROOT/build" -popd diff --git a/code/bin/build-mac.sh b/code/bin/build-mac.sh deleted file mode 100755 index adae80159..000000000 --- a/code/bin/build-mac.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -# If any command errors, stop the script -set -e - -# Set up directories -# NOTE(yuval): Replaced readlink with realpath which works for both macOS and Linux -ME="$(realpath "$0")" -LOCATION="$(dirname "$ME")" -SRC_ROOT="$(dirname "$LOCATION")" -PROJECT_ROOT="$(dirname "$SRC_ROOT")" -if [ ! -d "$PROJECT_ROOT/build" ]; then -mkdir "$PROJECT_ROOT/build" -fi -BUILD_ROOT="$PROJECT_ROOT/build" -BIN_ROOT="$SRC_ROOT/bin" -CUSTOM_ROOT="$SRC_ROOT/custom" -CUSTOM_BIN="$CUSTOM_ROOT/bin" - -# Get the build mode -BUILD_MODE="$1" -if [ -z "$BUILD_MODE" ]; then - BUILD_MODE="-DDEV_BUILD" -fi - -WARNINGS="-Wno-write-strings -Wno-comment -Wno-null-dereference -Wno-logical-op-parentheses -Wno-switch" - -FLAGS="-D_GNU_SOURCE -fPIC -fpermissive $BUILD_MODE" -INCLUDES="-I$SRC_ROOT -I$CUSTOM_ROOT" - -# Execute -clang++ $WARNINGS $FLAGS $INCLUDES "$BIN_ROOT/4ed_build.cpp" -g -o "$BUILD_ROOT/build" -pushd "$SRC_ROOT" -"$BUILD_ROOT/build" -popd diff --git a/code/bin/build.bat b/code/bin/build.bat deleted file mode 100644 index 6ddb30135..000000000 --- a/code/bin/build.bat +++ /dev/null @@ -1,52 +0,0 @@ -@echo off - -set location=%cd% -set me="%~dp0" - -REM 4cc\code\bin -cd %me% - -REM 4cc -cd ..\.. -set repo_root=%cd% -set src_root=%repo_root%\code -set build_root=%repo_root%\build -set bin_root=%repo_root%\code\bin -set custom_root=%repo_root%\code\custom -set custom_bin=%custom_root%\bin - -call %custom_bin%\setup_cl_x64.bat - -cd %location% - -set mode=%1 -set backend=%1 - -if "%1%" == "/DWIN32_OPENGL" ( - set mode=%2 -) else if "%1%" == "/DWIN32_DX11" ( - set mode=%2 -) else ( - set backend=%2 -) - -if "%backend%" == "" (set backend=/DWIN32_OPENGL) -if "%mode%" == "" (set mode=/DDEV_BUILD) - -set opts=/W4 /wd4310 /wd4100 /wd4201 /wd4505 /wd4996 /wd4127 /wd4510 /wd4512 /wd4610 /wd4390 /wd4189 /WX -set opts=%opts% /GR- /EHa- /nologo /FC /Zi -set opts=%opts% /I%src_root% /I%custom_root% -set opts=%opts% %mode% %backend% - -set FirstError=0 - -if not exist "%build_root%" mkdir %build_root% -pushd %build_root% -call cl %opts% kernel32.lib %bin_root%\4ed_build.cpp /Febuild -if %ERRORLEVEL% neq 0 (set FirstError=1) -if %ERRORLEVEL% neq 0 (goto END) -popd - -%build_root%\build -:END -if %ERRORLEVEL% neq 0 (set FirstError=1) diff --git a/code/bin/build_optimized.bat b/code/bin/build_optimized.bat deleted file mode 100644 index feff93732..000000000 --- a/code/bin/build_optimized.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off - -call bin\build.bat /DOPT_BUILD %1 diff --git a/code/bin/detect_os.sh b/code/bin/detect_os.sh deleted file mode 100755 index 2160aa9e2..000000000 --- a/code/bin/detect_os.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -os="unknown" -if [[ "$OSTYPE" == "darwin"* ]]; then -echo "mac" -elif [[ "$OSTYPE" == "linux-gnu" ]]; then -echo "linux" -fi - diff --git a/code/custom/4coder_base_types.cpp b/code/custom/4coder_base_types.cpp index 8ca2e84d7..710dc0a81 100644 --- a/code/custom/4coder_base_types.cpp +++ b/code/custom/4coder_base_types.cpp @@ -4685,6 +4685,7 @@ string_match(String_Const_char a, String_Const_char b){ } return(result); } + function b32 string_match(String_Const_u8 a, String_Const_u8 b){ b32 result = false; @@ -5179,6 +5180,11 @@ string_mod_replace_character(String_Const_char str, char o, char n){ } return(str); } + +bool string_has_prefix(String_Const_char s, String_Const_char prefix) { + return (string_match(string_prefix(s, prefix.size), prefix)); +} + function String_Const_u8 string_mod_replace_character(String_Const_u8 str, u8 o, u8 n){ for (u64 i = 0; i < str.size; i += 1){ diff --git a/code/custom/4coder_base_types.h b/code/custom/4coder_base_types.h index dd7be73c7..095480a4b 100644 --- a/code/custom/4coder_base_types.h +++ b/code/custom/4coder_base_types.h @@ -8,129 +8,180 @@ #define FCODER_BASE_TYPES //////////////////////////////// - #if defined(_MSC_VER) - # define COMPILER_CL 1 # if defined(_WIN32) # define OS_WINDOWS 1 # else -# error This compiler/platform combo is not supported yet +# error missing OS detection # endif # if defined(_M_AMD64) # define ARCH_X64 1 -# elif defined(_M_IX86) +# elif defined(_M_I86) # define ARCH_X86 1 -# elif defined(_M_ARM64) -# define ARCH_ARM64 1 # elif defined(_M_ARM) # define ARCH_ARM32 1 # else -# error architecture not supported yet +# error missing ARCH detection # endif -#elif defined(__clang__) - -# define COMPILER_CLANG 1 +#elif defined(__clang__) || defined(__GNUC__) +# if defined(__clang__) +# define COMPILER_CLANG 1 +# elif defined(__GNUC__) +# define COMPILER_GCC 1 +# endif -# if defined(__APPLE__) && defined(__MACH__) +# if defined(_WIN32) +# define OS_WINDOWS 1 +# elif defined(__gnu_linux__) || defined(__linux__) +# define OS_LINUX 1 +# elif defined(__APPLE__) && defined(__MACH__) # define OS_MAC 1 # else -# error This compiler/platform combo is not supported yet +# error missing OS detection # endif -# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) +# if defined(__amd64__) # define ARCH_X64 1 -# elif defined(i386) || defined(__i386) || defined(__i386__) +# elif defined(__i386__) # define ARCH_X86 1 -# elif defined(__aarch64__) -# define ARCH_ARM64 1 # elif defined(__arm__) # define ARCH_ARM32 1 -# else -# error architecture not supported yet -# endif - -#elif defined(__GNUC__) || defined(__GNUG__) - -# define COMPILER_GCC 1 - -# if defined(__gnu_linux__) -# define OS_LINUX 1 -# else -# error This compiler/platform combo is not supported yet -# endif - -# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) -# define ARCH_X64 1 -# elif defined(i386) || defined(__i386) || defined(__i386__) -# define ARCH_X86 1 # elif defined(__aarch64__) # define ARCH_ARM64 1 -# elif defined(__arm__) -# define ARCH_ARM32 1 # else -# error architecture not supported yet +# error missing ARCH detection # endif - #else -# error This compiler is not supported yet +# error no context cracking for this compiler #endif -#if defined(ARCH_X64) -# define ARCH_64BIT 1 -#elif defined(ARCH_X86) -# define ARCH_32BIT 1 - +#if !defined(COMPILER_CL) +# define COMPILER_CL 0 #endif - -// zeroify - -#if !defined(ARCH_32BIT) -#define ARCH_32BIT 0 +#if !defined(COMPILER_CLANG) +# define COMPILER_CLANG 0 +#endif +#if !defined(COMPILER_GCC) +# define COMPILER_GCC 0 +#endif +#if !defined(OS_WINDOWS) +# define OS_WINDOWS 0 #endif -#if !defined(ARCH_64BIT) -#define ARCH_64BIT 0 +#if !defined(OS_LINUX) +# define OS_LINUX 0 +#endif +#if !defined(OS_MAC) +# define OS_MAC 0 #endif #if !defined(ARCH_X64) -#define ARCH_X64 0 +# define ARCH_X64 0 #endif #if !defined(ARCH_X86) -#define ARCH_X86 0 +# define ARCH_X86 0 +#endif +#if !defined(ARCH_ARM32) +# define ARCH_ARM32 0 #endif #if !defined(ARCH_ARM64) -#define ARCH_ARM64 0 +# define ARCH_ARM64 0 #endif -#if !defined(ARCH_ARM32) -#define ARCH_ARM32 0 + +// language +#if defined(__cplusplus) +# define LANG_CXX 1 +#else +# define LANG_C 1 #endif -#if !defined(COMPILER_CL) -#define COMPILER_CL 0 + +#if !defined(LANG_CXX) +# define LANG_CXX 0 #endif -#if !defined(COMPILER_GCC) -#define COMPILER_GCC 0 +#if !defined(LANG_C) +# define LANG_C 0 #endif -#if !defined(COMPILER_CLANG) -#define COMPILER_CLANG 0 + +// language version: C +#if LANG_C +# if !defined(__STDC_VERSION__) +# define LANGVER_C90 1 +# elif (__STDC_VERSION__ == 199901) +# define LANGVER_C99 1 +# elif (__STDC_VERSION__ == 201112) +# define LANGVER_C11 1 +# elif (__STDC_VERSION__ == 201710) +# define LANGVER_C17 1 +# elif (__STDC_VERSION__ == 202000) +# define LANGVER_C23 1 +# else +# error unrecorgnized C version +# endif #endif -#if !defined(OS_WINDOWS) -#define OS_WINDOWS 0 + +#if !defined(LANGVER_C90) +# define LANGVER_C90 0 #endif -#if !defined(OS_LINUX) -#define OS_LINUX 0 +#if !defined(LANGVER_C99) +# define LANGVER_C99 0 #endif -#if !defined(OS_MAC) -#define OS_MAC 0 +#if !defined(LANGVER_C11) +# define LANGVER_C11 0 +#endif +#if !defined(LANGVER_C17) +# define LANGVER_C17 0 +#endif +#if !defined(LANGVER_C23) +# define LANGVER_C23 0 +#endif + +// language version: C++ +#if LANG_CXX +# if (__cplusplus == 199711) +# define LANGVER_CXX98 1 +# elif (__cplusplus == 201103) +# define LANGVER_CXX11 1 +# elif (__cplusplus == 201402) +# define LANGVER_CXX14 1 +# elif (__cplusplus == 201703) +# define LANGVER_CXX17 1 +# elif (__cplusplus == 202002) +# define LANGVER_CXX20 1 +# elif (__cplusplus == 202302) +# define LANGVER_CXX23 1 +# else +# error unrecorgnized C++ version +# endif +#endif + +#if !defined(LANGVER_CXX98) +# define LANGVER_CXX98 0 +#endif +#if !defined(LANGVER_CXX11) +# define LANGVER_CXX11 0 +#endif +#if !defined(LANGVER_CXX14) +# define LANGVER_CXX14 0 +#endif +#if !defined(LANGVER_CXX17) +# define LANGVER_CXX17 0 +#endif +#if !defined(LANGVER_CXX20) +# define LANGVER_CXX20 0 +#endif +#if !defined(LANGVER_CXX23) +# define LANGVER_CXX23 0 #endif #if !defined(SHIP_MODE) -#define SHIP_MODE 0 +# define SHIP_MODE 0 #else -#undef SHIP_MODE -#define SHIP_MODE 1 -#endif +# undef SHIP_MODE +# define SHIP_MODE 1 +#endif + // names diff --git a/code/custom/bin/setup_cl_generic.bat b/code/custom/bin/setup_cl_generic.bat index 151e64c2c..e266b24c5 100644 --- a/code/custom/bin/setup_cl_generic.bat +++ b/code/custom/bin/setup_cl_generic.bat @@ -35,3 +35,8 @@ IF NOT DEFINED LIB (IF EXIST "%VC_PATH%" (call "%VC_PATH%\VC\Auxiliary\Build\vcv SET VC_PATH=C:\Program Files\Microsoft Visual Studio\2022\Professional IF NOT DEFINED LIB (IF EXIST "%VC_PATH%" (call "%VC_PATH%\VC\Auxiliary\Build\vcvarsall.bat" %1)) +SET VC_PATH=C:\Program Files\Microsoft Visual Studio\18\Community +IF NOT DEFINED LIB (IF EXIST "%VC_PATH%" (call "%VC_PATH%\VC\Auxiliary\Build\vcvarsall.bat" %1)) + +SET VC_PATH=C:\Program Files\Microsoft Visual Studio\18\Professional +IF NOT DEFINED LIB (IF EXIST "%VC_PATH%" (call "%VC_PATH%\VC\Auxiliary\Build\vcvarsall.bat" %1)) \ No newline at end of file diff --git a/code/custom/bin/setup_cl_x86.bat b/code/custom/bin/setup_cl_x86.bat index 3266985f3..d1102d45e 100644 --- a/code/custom/bin/setup_cl_x86.bat +++ b/code/custom/bin/setup_cl_x86.bat @@ -1,5 +1,8 @@ @echo off +where /q cl +IF %ERRORLEVEL% == 0 (EXIT /b) + SET SCRIPTS_PATH=%~dp0 "%SCRIPTS_PATH%\setup_cl_generic.bat" amd64_x86 diff --git a/code/platform_linux/linux_4ed.cpp b/code/platform_linux/linux_4ed.cpp index 71fe39ec6..430baea62 100644 --- a/code/platform_linux/linux_4ed.cpp +++ b/code/platform_linux/linux_4ed.cpp @@ -91,6 +91,7 @@ #include #include #include +#include #include #include #define function static @@ -156,6 +157,18 @@ struct Linux_Memory_Tracker_Node { u64 size; }; +struct Linux_Scroll_Info{ + int device_id; + int valuator_number; // from XIScrollClassInfo.number + int scroll_type; // from XIScrollClassInfo.scroll_type + double increment; // from XIScrollClassInfo.increment + double last_value; + b8 has_last_value; +}; + +// NOTE(edye): Arbitrary cap on scroll devices +#define MAX_SCROLL_DEVICES 16 + struct Linux_Vars { Thread_Context tctx; Arena frame_arena; @@ -181,6 +194,11 @@ struct Linux_Vars { int step_timer_fd; u64 last_step_time; + b8 has_smooth_scroll; + i32 num_scroll_devices; + int xinput_opcode; // store to check GenericEvent types + Linux_Scroll_Info scroll_device_info[MAX_SCROLL_DEVICES]; + XCursor xcursors[APP_MOUSE_CURSOR_COUNT]; Application_Mouse_Cursor cursor; XCursor hidden_cursor; @@ -744,6 +762,34 @@ glx_create_context(GLXFBConfig fb_config){ //////////////////////////// +internal void +linux_init_scroll_devices(){ + // track each scroll device for precise scrolling + linuxvars.num_scroll_devices = 0; + int num_devices = 0; + XIDeviceInfo *info = XIQueryDevice(linuxvars.dpy, XIAllMasterDevices, &num_devices); + for (i32 i = 0; i < num_devices; i++) { + for (i32 j = 0; j < info[i].num_classes; j++) { + if (info[i].classes[j]->type == XIScrollClass) { + if(linuxvars.num_scroll_devices < MAX_SCROLL_DEVICES){ + + XIScrollClassInfo *scroll = (XIScrollClassInfo*)info[i].classes[j]; + + Linux_Scroll_Info *si = &linuxvars.scroll_device_info[linuxvars.num_scroll_devices]; + si->device_id = info[i].deviceid; + si->valuator_number = scroll->number; + si->scroll_type = scroll->scroll_type; + si->increment = scroll->increment; + si->has_last_value = false; + + linuxvars.num_scroll_devices++; + } + } + } + } + if (info) XIFreeDeviceInfo(info); +} + internal void linux_x11_init(int argc, char** argv, Plat_Settings* settings) { @@ -963,6 +1009,44 @@ linux_x11_init(int argc, char** argv, Plat_Settings* settings) { XSelectInput(linuxvars.dpy, linuxvars.win, event_mask); + // init smooth scrolling + + int xi_opcode, xi_firstevent, xi_firsterror; + if (XQueryExtension(linuxvars.dpy, "XInputExtension", &xi_opcode, &xi_firstevent, &xi_firsterror)) { + int major = 2, minor = 1; + b8 query_success = (XIQueryVersion(linuxvars.dpy, &major, &minor) == Success); + linuxvars.has_smooth_scroll = query_success && (major > 2 || (major == 2 && minor >= 1)); + + if (linuxvars.has_smooth_scroll) { + linuxvars.has_smooth_scroll = true; + linuxvars.xinput_opcode = xi_opcode; + + // track XI_Motion events for smooth scrolling + XIEventMask xi_mask; + unsigned char mask_bytes[XIMaskLen(XI_LASTEVENT)] = { 0 }; + + xi_mask.deviceid = XIAllMasterDevices; + xi_mask.mask_len = sizeof(mask_bytes); + xi_mask.mask = mask_bytes; + + XISetMask(xi_mask.mask, XI_Motion); + XISelectEvents(linuxvars.dpy, linuxvars.win, &xi_mask, 1); + + // track XI_HierarchyChanged events to detect when devices change + XIEventMask xi_hierarchy_mask; + unsigned char hier_mask_bytes[XIMaskLen(XI_LASTEVENT)] = { 0 }; + + xi_hierarchy_mask.deviceid = XIAllDevices; + xi_hierarchy_mask.mask_len = sizeof(hier_mask_bytes); + xi_hierarchy_mask.mask = hier_mask_bytes; + + XISetMask(xi_hierarchy_mask.mask, XI_HierarchyChanged); + XISelectEvents(linuxvars.dpy, DefaultRootWindow(linuxvars.dpy), &xi_hierarchy_mask, 1); + } + } + + linux_init_scroll_devices(); + // init XKB keyboard extension if(!XkbQueryExtension(linuxvars.dpy, 0, &linuxvars.xkb_event, 0, 0, 0)) { @@ -1428,6 +1512,56 @@ linux_handle_x11_events() { u64 event_id = (u64)event.xkey.serial << 32 | event.xkey.time; + if (linuxvars.has_smooth_scroll && + (event.xcookie.type == GenericEvent) && + (event.xcookie.extension == linuxvars.xinput_opcode) && + XGetEventData(linuxvars.dpy, &event.xcookie)){ + + if (event.xcookie.evtype == XI_Motion) { + XIDeviceEvent *xinput_device = (XIDeviceEvent *)event.xcookie.data; + double *values = xinput_device->valuators.values; + int value_idx = 0; + + for (int v = 0; v < xinput_device->valuators.mask_len * 8; v++) { + if (!XIMaskIsSet(xinput_device->valuators.mask, v)) continue; + + double raw_value = values[value_idx]; + value_idx++; + + // find registered scroll axis + for (int i = 0; i < linuxvars.num_scroll_devices; i++) { + Linux_Scroll_Info *si = &linuxvars.scroll_device_info[i]; + if (si->device_id != xinput_device->deviceid) continue; + if (si->valuator_number != v) continue; + + if (!si->has_last_value) { + si->last_value = raw_value; + si->has_last_value = true; + return; + } + + double delta_raw = raw_value - si->last_value; + si->last_value = raw_value; + + double scroll_sensitivity = 30.0; // NOTE(edye): arbitrary, could be changed to a setting + double scroll_amount = delta_raw / si->increment * scroll_sensitivity; + + if (si->scroll_type == XIScrollTypeVertical) { + linuxvars.input.trans.mouse_wheel_y += scroll_amount; + } else if (si->scroll_type == XIScrollTypeHorizontal) { + linuxvars.input.trans.mouse_wheel_x -= scroll_amount; + } + } + } + } else if (event.xcookie.evtype == XI_HierarchyChanged) { + // devices have been changed + // reinitialize the scroll devices + linux_init_scroll_devices(); + } + + XFreeEventData(linuxvars.dpy, &event.xcookie); + } + switch(event.type) { case KeyPress: { should_step = true; @@ -1581,20 +1715,28 @@ linux_handle_x11_events() { } break; case Button4: { - linuxvars.input.trans.mouse_wheel_y = -100; + if(!linuxvars.has_smooth_scroll) { + linuxvars.input.trans.mouse_wheel_y = -100; + } } break; case Button5: { - linuxvars.input.trans.mouse_wheel_y = +100; + if(!linuxvars.has_smooth_scroll) { + linuxvars.input.trans.mouse_wheel_y = +100; + } } break; // NOTE(FS): 6 and 7 are undocumented but generally accepted as horizontal scroll buttons. case 6: { - linuxvars.input.trans.mouse_wheel_x = -100; + if(!linuxvars.has_smooth_scroll) { + linuxvars.input.trans.mouse_wheel_x = -100; + } } break; case 7: { - linuxvars.input.trans.mouse_wheel_x = +100; + if(!linuxvars.has_smooth_scroll) { + linuxvars.input.trans.mouse_wheel_x = +100; + } } break; } } break; diff --git a/code/platform_linux/linux_4ed_functions.cpp b/code/platform_linux/linux_4ed_functions.cpp index d52e20637..b37997e58 100644 --- a/code/platform_linux/linux_4ed_functions.cpp +++ b/code/platform_linux/linux_4ed_functions.cpp @@ -64,67 +64,37 @@ system_get_path(Arena* arena, System_Path_Code path_code){ internal String_Const_u8 system_get_canonical(Arena* arena, String_Const_u8 name){ + Assert(name.size < PATH_MAX - 1); + char tmp[PATH_MAX] = { 0 }; + strncpy(tmp, (const char *)name.str, name.size); + + char real[PATH_MAX] = { 0 }; + realpath(tmp, real); - // first remove redundant ../, //, ./ parts - - const u8* input = (u8*) strndupa((char*)name.str, name.size); - u8* output = push_array(arena, u8, name.size + 1); - - const u8* p = input; - u8* q = output; - - while(*p) { - - // not a slash - copy char - if(p[0] != '/') { - *q++ = *p++; - continue; - } - - // two slashes in a row, skip one. - if(p[1] == '/') { - ++p; - } - else if(p[1] == '.') { - - // skip "/./" or trailing "/." - if(p[2] == '/' || p[2] == '\0') { - p += 2; - } - - // if we encounter "/../" or trailing "/..", remove last directory instead - else if(p[2] == '.' && (p[3] == '/' || p[3] == '\0')) { - while(q > output && *--q != '/'){}; - p += 3; - } - - else { - *q++ = *p++; - } - } - else { - *q++ = *p++; - } - } - + String_Const_u8 result = push_string_copy(arena, + SCu8((u8*)real, cstring_length(real))); #ifdef INSO_DEBUG - if(name.size != q - output) { - LINUX_FN_DEBUG("[%.*s] -> [%.*s]", (int)name.size, name.str, (int)(q - output), output); + if(name.size != result.size) { + LINUX_FN_DEBUG("[%.*s] -> [%.*s]", + (int)name.size, name.str, + (int)result.size, result.str); } #endif - - // TODO: use realpath at this point to resolve symlinks? - return SCu8(output, q - output); + return result; } internal File_List system_get_file_list(Arena* arena, String_Const_u8 directory){ + Assert(directory.size < PATH_MAX - 1); //LINUX_FN_DEBUG("%.*s", (int)directory.size, directory.str); File_List result = {}; - char* path = strndupa((char*)directory.str, directory.size); + char path[PATH_MAX] = { 0 }; + strncpy(path, (const char *)directory.str, directory.size); + int fd = open(path, O_RDONLY | O_DIRECTORY); if(fd == -1) { + LINUX_FN_DEBUG("Failed to open directory: %.*s", (int)directory.size, directory.str); perror("open"); return result; } @@ -168,7 +138,7 @@ system_get_file_list(Arena* arena, String_Const_u8 directory){ *fip++ = f; } - qsort(result.infos, result.count, sizeof(File_Info*), (__compar_fn_t)&linux_compare_file_infos); + qsort(result.infos, result.count, sizeof(File_Info*), (int(*)(const void*, const void*))&linux_compare_file_infos); for(u32 i = 0; i < result.count - 1; ++i) { result.infos[i]->next = result.infos[i+1]; @@ -252,6 +222,7 @@ system_save_file(Arena* scratch, char* file_name, String_Const_u8 data){ } close(fd); } else { + LINUX_FN_DEBUG("Failed to open file: %s", file_name); perror("open"); } @@ -334,7 +305,7 @@ function system_universal_date_time_from_local_sig(){ struct tm local_tm = {}; linux_tm_from_date_time(&local_tm, date_time); - time_t loc_time = timelocal(&local_tm); + time_t loc_time = mktime(&local_tm); struct tm *utc_tm = gmtime(&loc_time); Date_Time result = {}; linux_date_time_from_tm(&result, utc_tm);