diff --git a/.github/workflows/run-tests-macos.yml b/.github/workflows/run-tests-macos.yml index b085d3f..e631870 100644 --- a/.github/workflows/run-tests-macos.yml +++ b/.github/workflows/run-tests-macos.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: - compiler: [gcc, clang] + compiler: [clang] steps: - name: Checkout code with submodules @@ -33,6 +33,7 @@ jobs: "04-raylib-build" "05-samurai-source-code" "06-lua-source-code" + "07-raylib-source-code" ) for test in "${tests[@]}"; do diff --git a/mate.h b/mate.h index a159f37..69e9b96 100644 --- a/mate.h +++ b/mate.h @@ -2377,6 +2377,8 @@ typedef struct { OptimizationFlag optimization; } StaticLibOptions; +typedef enum { none = 0, needed, weak } LinkFrameworkOptions; + typedef StringBuilder FlagBuilder; /* --- Build System --- */ @@ -2422,6 +2424,31 @@ static void mateAddLibraryPaths(String *targetLibs, StringVector *libs); } while (0) static void mateLinkSystemLibraries(String *targetLibs, StringVector *libs); + +#define LinkFrameworks(target, ...) \ + do { \ + StringVector _frameworks = {0}; \ + StringVectorPushMany(_frameworks, __VA_ARGS__); \ + /* Frameworks are just specialized library bundles. */ \ + mateLinkFrameworks(&(target).libs, &_frameworks); \ + \ + /* Cleanup */ \ + VecFree(_frameworks); \ + } while (0) +static void mateLinkFrameworks(String *targetLibs, StringVector *frameworks); + +#define LinkFrameworksWithOptions(target, options, ...) \ + do { \ + StringVector _frameworks = {0}; \ + StringVectorPushMany(_frameworks, __VA_ARGS__); \ + /* Frameworks are just specialized library bundles. */ \ + mateLinkFrameworksWithOptions(&(target).libs, options, &_frameworks); \ + \ + /* Cleanup */ \ + VecFree(_frameworks); \ + } while (0) +static void mateLinkFrameworksWithOptions(String *targetLibs, LinkFrameworkOptions options, StringVector *frameworks); + #define AddIncludePaths(target, ...) \ do { \ StringVector _includes = {0}; \ @@ -2433,6 +2460,18 @@ static void mateLinkSystemLibraries(String *targetLibs, StringVector *libs); } while (0) static void mateAddIncludePaths(String *targetIncludes, StringVector *vector); +#define AddFrameworkPaths(target, ...) \ + do { \ + StringVector _frameworks = {0}; \ + StringVectorPushMany(_frameworks, __VA_ARGS__); \ + /* Frameworks are just specialized library bundles. */ \ + mateAddFrameworkPaths(&(target).includes, &_frameworks); \ + \ + /* Cleanup */ \ + VecFree(_frameworks); \ + } while (0) +static void mateAddFrameworkPaths(String *targetIncludes, StringVector *includes); + #define AddFile(target, source) mateAddFile(&(target).sources, s(source)); static void mateAddFile(StringVector *sources, String source); @@ -6517,6 +6556,51 @@ static void mateLinkSystemLibraries(String *targetLibs, StringVector *libs) { *targetLibs = builder.buffer; } +static void mateLinkFrameworks(String *targetLibs, StringVector *frameworks) { + mateLinkFrameworksWithOptions(targetLibs, none, frameworks); +} + +static void mateLinkFrameworksWithOptions(String *targetLibs, LinkFrameworkOptions options, StringVector *frameworks) { + Assert(!isGCC(), + "LinkFrameworks: Automatic framework linking is not supported by GCC. " + "Use standard linking functions after adding a framework path instead."); + Assert(isClang(), "LinkFrameworks: This function is only supported for Clang."); + + char *frameworkFlag = NULL; + switch (options) { + case none: + frameworkFlag = "-framework"; + break; + case needed: + frameworkFlag = "-needed_framework"; + break; + case weak: + frameworkFlag = "-weak_framework"; + break; + default: + Assert(0, "LinkFrameworks: Invalid framework linking option provided."); + } + + StringBuilder builder = StringBuilderCreate(mateState.arena); + + if (targetLibs->length) { + StringBuilderAppend(mateState.arena, &builder, targetLibs); + } + + for (size_t i = 0; i < frameworks->length; i++) { + String currFW = VecAt((*frameworks), i); + if (i == 0 && builder.buffer.length == 0) { + String buffer = F(mateState.arena, "%s %s", frameworkFlag, currFW.data); + StringBuilderAppend(mateState.arena, &builder, &buffer); + continue; + } + String buffer = F(mateState.arena, " %s %s", frameworkFlag, currFW.data); + StringBuilderAppend(mateState.arena, &builder, &buffer); + } + + *targetLibs = builder.buffer; +} + static void mateAddIncludePaths(String *targetIncludes, StringVector *includes) { StringBuilder builder = StringBuilderCreate(mateState.arena); @@ -6554,6 +6638,31 @@ static void mateAddIncludePaths(String *targetIncludes, StringVector *includes) *targetIncludes = builder.buffer; } +static void mateAddFrameworkPaths(String *targetIncludes, StringVector *includes) { + Assert(isClang() || isGCC(), "AddFrameworkPaths: This function is only supported for GCC/Clang."); + + StringBuilder builder = StringBuilderCreate(mateState.arena); + + if (targetIncludes->length) { + StringBuilderAppend(mateState.arena, &builder, targetIncludes); + StringBuilderAppend(mateState.arena, &builder, &S(" ")); + } + + // GCC/Clang format: -F"path" + for (size_t i = 0; i < includes->length; i++) { + String currInclude = VecAt((*includes), i); + if (i == 0 && builder.buffer.length == 0) { + String buffer = F(mateState.arena, "-F\"%s\"", currInclude.data); + StringBuilderAppend(mateState.arena, &builder, &buffer); + continue; + } + String buffer = F(mateState.arena, " -F\"%s\"", currInclude.data); + StringBuilderAppend(mateState.arena, &builder, &buffer); + } + + *targetIncludes = builder.buffer; +} + void EndBuild(void) { LogInfo("Build took: " FMT_I64 "ms", mateState.totalTime); ArenaFree(mateState.arena); diff --git a/scripts/run-tests-macos.sh b/scripts/run-tests-macos.sh index 0352812..01b4e3d 100755 --- a/scripts/run-tests-macos.sh +++ b/scripts/run-tests-macos.sh @@ -22,7 +22,7 @@ tests=( "04-raylib-build" "05-samurai-source-code" "06-lua-source-code" - # "07-raylib-source-code" # Requires framework linking on macOS, which isnt implemented yet + "07-raylib-source-code" ) echo "NOTE: Skipping 07-raylib-source-code test due to missing framework linking support" diff --git a/src/api.c b/src/api.c index 177d1a8..61d41fd 100644 --- a/src/api.c +++ b/src/api.c @@ -1104,6 +1104,51 @@ static void mateLinkSystemLibraries(String *targetLibs, StringVector *libs) { *targetLibs = builder.buffer; } +static void mateLinkFrameworks(String *targetLibs, StringVector *frameworks) { + mateLinkFrameworksWithOptions(targetLibs, none, frameworks); +} + +static void mateLinkFrameworksWithOptions(String *targetLibs, LinkFrameworkOptions options, StringVector *frameworks) { + Assert(!isGCC(), + "LinkFrameworks: Automatic framework linking is not supported by GCC. " + "Use standard linking functions after adding a framework path instead."); + Assert(isClang(), "LinkFrameworks: This function is only supported for Clang."); + + char *frameworkFlag = NULL; + switch (options) { + case none: + frameworkFlag = "-framework"; + break; + case needed: + frameworkFlag = "-needed_framework"; + break; + case weak: + frameworkFlag = "-weak_framework"; + break; + default: + Assert(0, "LinkFrameworks: Invalid framework linking option provided."); + } + + StringBuilder builder = StringBuilderCreate(mateState.arena); + + if (targetLibs->length) { + StringBuilderAppend(mateState.arena, &builder, targetLibs); + } + + for (size_t i = 0; i < frameworks->length; i++) { + String currFW = VecAt((*frameworks), i); + if (i == 0 && builder.buffer.length == 0) { + String buffer = F(mateState.arena, "%s %s", frameworkFlag, currFW.data); + StringBuilderAppend(mateState.arena, &builder, &buffer); + continue; + } + String buffer = F(mateState.arena, " %s %s", frameworkFlag, currFW.data); + StringBuilderAppend(mateState.arena, &builder, &buffer); + } + + *targetLibs = builder.buffer; +} + static void mateAddIncludePaths(String *targetIncludes, StringVector *includes) { StringBuilder builder = StringBuilderCreate(mateState.arena); @@ -1141,6 +1186,31 @@ static void mateAddIncludePaths(String *targetIncludes, StringVector *includes) *targetIncludes = builder.buffer; } +static void mateAddFrameworkPaths(String *targetIncludes, StringVector *includes) { + Assert(isClang() || isGCC(), "AddFrameworkPaths: This function is only supported for GCC/Clang."); + + StringBuilder builder = StringBuilderCreate(mateState.arena); + + if (targetIncludes->length) { + StringBuilderAppend(mateState.arena, &builder, targetIncludes); + StringBuilderAppend(mateState.arena, &builder, &S(" ")); + } + + // GCC/Clang format: -F"path" + for (size_t i = 0; i < includes->length; i++) { + String currInclude = VecAt((*includes), i); + if (i == 0 && builder.buffer.length == 0) { + String buffer = F(mateState.arena, "-F\"%s\"", currInclude.data); + StringBuilderAppend(mateState.arena, &builder, &buffer); + continue; + } + String buffer = F(mateState.arena, " -F\"%s\"", currInclude.data); + StringBuilderAppend(mateState.arena, &builder, &buffer); + } + + *targetIncludes = builder.buffer; +} + void EndBuild(void) { LogInfo("Build took: " FMT_I64 "ms", mateState.totalTime); ArenaFree(mateState.arena); diff --git a/src/api.h b/src/api.h index a8f21e9..ce081cc 100644 --- a/src/api.h +++ b/src/api.h @@ -140,6 +140,8 @@ typedef struct { OptimizationFlag optimization; } StaticLibOptions; +typedef enum { none = 0, needed, weak } LinkFrameworkOptions; + typedef StringBuilder FlagBuilder; /* --- Build System --- */ @@ -185,6 +187,31 @@ static void mateAddLibraryPaths(String *targetLibs, StringVector *libs); } while (0) static void mateLinkSystemLibraries(String *targetLibs, StringVector *libs); + +#define LinkFrameworks(target, ...) \ + do { \ + StringVector _frameworks = {0}; \ + StringVectorPushMany(_frameworks, __VA_ARGS__); \ + /* Frameworks are just specialized library bundles. */ \ + mateLinkFrameworks(&(target).libs, &_frameworks); \ + \ + /* Cleanup */ \ + VecFree(_frameworks); \ + } while (0) +static void mateLinkFrameworks(String *targetLibs, StringVector *frameworks); + +#define LinkFrameworksWithOptions(target, options, ...) \ + do { \ + StringVector _frameworks = {0}; \ + StringVectorPushMany(_frameworks, __VA_ARGS__); \ + /* Frameworks are just specialized library bundles. */ \ + mateLinkFrameworksWithOptions(&(target).libs, options, &_frameworks); \ + \ + /* Cleanup */ \ + VecFree(_frameworks); \ + } while (0) +static void mateLinkFrameworksWithOptions(String *targetLibs, LinkFrameworkOptions options, StringVector *frameworks); + #define AddIncludePaths(target, ...) \ do { \ StringVector _includes = {0}; \ @@ -196,6 +223,18 @@ static void mateLinkSystemLibraries(String *targetLibs, StringVector *libs); } while (0) static void mateAddIncludePaths(String *targetIncludes, StringVector *vector); +#define AddFrameworkPaths(target, ...) \ + do { \ + StringVector _frameworks = {0}; \ + StringVectorPushMany(_frameworks, __VA_ARGS__); \ + /* Frameworks are just specialized library bundles. */ \ + mateAddFrameworkPaths(&(target).includes, &_frameworks); \ + \ + /* Cleanup */ \ + VecFree(_frameworks); \ + } while (0) +static void mateAddFrameworkPaths(String *targetIncludes, StringVector *includes); + #define AddFile(target, source) mateAddFile(&(target).sources, s(source)); static void mateAddFile(StringVector *sources, String source); diff --git a/tests/07-raylib-source-code/mate.c b/tests/07-raylib-source-code/mate.c index 8ca9754..b2a504d 100644 --- a/tests/07-raylib-source-code/mate.c +++ b/tests/07-raylib-source-code/mate.c @@ -15,7 +15,11 @@ static char *GetCFlags(void) { if (isLinux()) { FlagBuilderAdd(&flagsBuilder, "DPLATFORM_DESKTOP_GLFW", "D_GLFW_X11"); } - + if (isMacOs()) { + FlagBuilderAdd(&flagsBuilder, "DPLATFORM_DESKTOP_GLFW", "D_GL_SILENCE_DEPRECATION=1"); + // Required due to raylib's use of Objective-C types on macOS. + FlagBuilderAdd(&flagsBuilder, "x objective-c"); + } if (isWindows()) { FlagBuilderAdd(&flagsBuilder, "DPLATFORM_DESKTOP_GLFW"); } @@ -27,7 +31,10 @@ i32 main(void) { StartBuild(); { { // Compile static lib - StaticLib staticLib = CreateStaticLib((StaticLibOptions){.output = "libraylib", .flags = GetCFlags()}); + StaticLib staticLib = CreateStaticLib((StaticLibOptions){ + .output = "libraylib", + .flags = GetCFlags(), + }); AddFile(staticLib, "./src/rcore.c"); AddFile(staticLib, "./src/utils.c"); @@ -45,6 +52,12 @@ i32 main(void) { if (isLinux()) { LinkSystemLibraries(staticLib, "GL", "rt", "dl", "m", "X11", "Xcursor", "Xext", "Xfixes", "Xi", "Xinerama", "Xrandr", "Xrender"); } + if (isMacOs()) { + // Link regular system libraries. + LinkSystemLibraries(staticLib, "m"); + // Link macOS system frameworks. + LinkFrameworks(staticLib, "Foundation", "AppKit", "IOKit", "OpenGL", "CoreVideo"); + } if (isWindows()) { LinkSystemLibraries(staticLib, "winmm", "gdi32", "opengl32"); } @@ -67,6 +80,12 @@ i32 main(void) { if (isLinux()) { LinkSystemLibraries(executable, "raylib", "GL", "rt", "dl", "m", "X11"); } + if (isMacOs()) { + // Link regular system libraries. + LinkSystemLibraries(executable, "raylib", "m"); + // Link macOS system frameworks. + LinkFrameworks(executable, "CoreVideo", "IOKit", "Cocoa", "GLUT", "OpenGL"); + } if (isWindows()) { LinkSystemLibraries(executable, "raylib", "winmm", "gdi32", "opengl32"); }