Build uses C++ to imperatively describe a sequence of build operations.
#include "SC-build.h"
#include "Libraries/Strings/StringBuilder.h"
#include "SC-package.h"
namespace SC
{
namespace Tools
{
[[nodiscard]] Result installSokol(const Build::Directories& directories, Package& package)
{
Download download;
download.packagesCacheDirectory = directories.packagesCacheDirectory;
download.packagesInstallDirectory = directories.packagesInstallDirectory;
download.packageName = "sokol";
download.packageVersion = "7b5cfa7";
download.url = "https://github.com/floooh/sokol.git";
download.isGitClone = true;
download.createLink = false;
package.packageBaseName = "sokol";
CustomFunctions functions;
functions.testFunction = &verifyGitCommitHash;
SC_TRY(packageInstall(download, package, functions));
return Result(true);
}
[[nodiscard]] Result installDearImGui(const Build::Directories& directories, Package& package)
{
Download download;
download.packagesCacheDirectory = directories.packagesCacheDirectory;
download.packagesInstallDirectory = directories.packagesInstallDirectory;
download.packageName = "dear-imgui";
download.packageVersion = "00ad3c6";
download.url = "https://github.com/ocornut/imgui.git";
download.isGitClone = true;
download.createLink = false;
package.packageBaseName = "dear-imgui";
CustomFunctions functions;
functions.testFunction = &verifyGitCommitHash;
SC_TRY(packageInstall(download, package, functions));
return Result(true);
}
}
namespace Build
{
void addSaneCppLibraries(Project& project, const Parameters& parameters)
{
project.addDirectory("Bindings/c", "**.cpp");
project.addDirectory("Bindings/c", "**.c");
project.addDirectory("Bindings/c", "**.h");
project.addDirectory("Libraries", "**.cpp");
project.addDirectory("Libraries", "**.h");
project.addDirectory("Libraries", "**.inl");
project.addDirectory("LibrariesExtra", "**.h");
project.addDirectory("LibrariesExtra", "**.cpp");
project.addDirectory("Support/DebugVisualizers", "*.cpp");
if (parameters.platform == Platform::Apple)
{
project.link.addFrameworks({"CoreFoundation", "CoreServices"});
}
if (parameters.platform != Platform::Windows)
{
project.link.addLibraries({"dl", "pthread"});
}
{
project.addDirectory("Support/DebugVisualizers/MSVC", "*.natvis");
}
else
{
project.addDirectory("Support/DebugVisualizers/LLDB", "*");
}
}
static constexpr StringView TEST_PROJECT_NAME = "SCTest";
Result buildTestProject(const Parameters& parameters, Project& project)
{
project.setRootDirectory(parameters.directories.libraryDirectory.view());
project.compile.addDefines({"SC_LIBRARY_PATH=$(PROJECT_ROOT)", "SC_COMPILER_ENABLE_CONFIG=1"});
project.compile.addIncludes({
".",
"Tests/SCTest",
});
addSaneCppLibraries(project, parameters);
project.addDirectory("Tests/SCTest", "*.cpp");
project.addDirectory("Tests/SCTest", "*.h");
project.addDirectory("Tools", "SC-*.cpp");
project.addDirectory("Tools", "*.h");
project.addDirectory("Tools", "*Test.cpp");
return Result(true);
}
static constexpr StringView EXAMPLE_PROJECT_NAME = "SCExample";
Result buildExampleProject(const Parameters& parameters, Project& project)
{
project.setRootDirectory(parameters.directories.libraryDirectory.view());
project.iconPath = "Documentation/Doxygen/SC.svg";
Tools::Package sokol;
SC_TRY(Tools::installSokol(parameters.directories, sokol));
Tools::Package dearImGui;
SC_TRY(Tools::installDearImGui(parameters.directories, dearImGui));
project.compile.addIncludes({".", sokol.packageLocalDirectory.view(), dearImGui.packageLocalDirectory.view()});
addSaneCppLibraries(project, parameters);
project.removeFiles("Bindings/c", "*");
project.removeFiles("Libraries", "**Test.cpp");
project.removeFiles("LibrariesExtra", "**Test.cpp");
project.removeFiles("Support", "**Test.cpp");
project.addDirectory(dearImGui.packageLocalDirectory.view(), "*.cpp");
project.addDirectory(sokol.packageLocalDirectory.view(), "*.h");
String imguiRelative, imguiDefine;
Path::AsNative));
SC_TRY(StringBuilder(imguiDefine).format(
"SC_IMGUI_PATH=$(PROJECT_ROOT)/{}", imguiRelative));
project.compile.addDefines({"SC_LIBRARY_PATH=$(PROJECT_ROOT)", imguiDefine.view()});
if (parameters.platform == Platform::Apple)
{
project.addDirectory("Examples/SCExample", "*.m");
project.link.addFrameworks({"Metal", "MetalKit", "QuartzCore"});
project.link.addFrameworks({"Cocoa"}, PlatformApple::macOS);
project.link.addFrameworks({"UIKit", "Foundation"}, PlatformApple::iOS);
}
else
{
project.addDirectory("Examples/SCExample", "*.c");
if (parameters.platform == Platform::Linux)
{
project.link.addLibraries({"GL", "EGL", "X11", "Xi", "Xcursor"});
}
}
if (parameters.platform == Platform::Windows)
{
project.compile.addDefines({"IMGUI_API=__declspec( dllexport )"});
}
else
{
project.compile.addDefines({"IMGUI_API=__attribute__((visibility(\"default\")))"});
}
project.addDirectory("Examples/SCExample", "**.h");
project.addDirectory("Examples/SCExample", "**.cpp");
return Result(true);
}
Result configure(Definition& definition, const Parameters& parameters)
{
Workspace workspace = {"SCTest"};
SC_TRY(workspace.projects.resize(2));
SC_TRY(buildTestProject(parameters, workspace.projects[0]));
SC_TRY(buildExampleProject(parameters, workspace.projects[1]));
definition.workspaces.push_back(
move(workspace));
return Result(true);
}
Result executeAction(const Action& action) { return Build::Action::execute(action, configure, TEST_PROJECT_NAME); }
}
}
#define SC_COMPILER_WARNING_POP
Pops warning from inside a macro.
Definition: Compiler.h:107
#define SC_COMPILER_WARNING_PUSH_UNUSED_RESULT
Disables unused-result warning (due to ignoring a return value marked as [[nodiscard]])
Definition: Compiler.h:146
constexpr T && move(T &value)
Converts an lvalue to an rvalue reference.
Definition: Compiler.h:269
#define SC_TRY(expression)
Checks the value of the given expression and if failed, returns this value to caller.
Definition: Result.h:47
@ Debug
Debug configuration.
@ Release
Release configuration.
@ DebugCoverage
Debug coverage configuration.
@ VisualStudio2022
Generate projects for Visual Studio 2022.
Definition: Build.h:82
@ guiApplication
gui application
Definition: Build.h:201
@ Executable
Create executable program.
Definition: Build.h:334
static bool relativeFromTo(StringView source, StringView destination, String &output, Type type, Type outputType=AsNative)
Get relative path that appended to source resolves to destination.
So far the entire build configuration is created in C++ but each invocation with a different set of "build parameters" it's building a data structure that is free of conditionals, as they've been evaluated by the imperative code. Such "post-configure" build settings could be serialized to JSON (or using binary Serialization) or to any other declarative format if needed.