Build uses C++ to imperatively describe a sequence of build operations.
#include "SC-build.h"
#include "../Libraries/FileSystemIterator/FileSystemIterator.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 = "d5863cb";
download.shallowClone = "d5863cb78ea1552558c81d6db780dfcec49557ce";
download.url = "https://github.com/floooh/sokol.git";
download.isGitClone = true;
download.createLink = false;
package.packageBaseName = "sokol";
CustomFunctions functions;
functions.testFunction = &verifyGitCommitHashCache;
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 = "af987eb";
download.url = "https://github.com/ocornut/imgui.git";
download.shallowClone = "af987eb1176fb4c11a6f0a4f2550d9907d113df5";
download.isGitClone = true;
download.createLink = false;
package.packageBaseName = "dear-imgui";
CustomFunctions functions;
functions.testFunction = &verifyGitCommitHashCache;
SC_TRY(packageInstall(download, package, functions));
return Result(true);
}
}
namespace Build
{
void addSaneCppLibraries(Project& project, const Parameters& parameters)
{
project.addFiles("Libraries", "**.cpp");
project.addFiles("Libraries", "**.h");
project.addFiles("Libraries", "**.inl");
project.addFiles("LibrariesExtra", "**.h");
project.addFiles("LibrariesExtra", "**.cpp");
if (parameters.platform == Platform::Apple)
{
project.addLinkFrameworks({"CoreFoundation", "CoreServices"});
}
if (parameters.platform != Platform::Windows)
{
project.addLinkLibraries({"dl", "pthread"});
}
{
project.addFiles("Support/DebugVisualizers/MSVC", "*.natvis");
}
else
{
project.addFiles("Support/DebugVisualizers/LLDB", "*");
}
}
static constexpr StringView TEST_PROJECT_NAME = "SCTest";
Result buildTestProject(const Parameters& parameters, Project& project)
{
project.setRootDirectory(parameters.directories.libraryDirectory.view());
project.addDefines({"SC_LIBRARY_PATH=$(PROJECT_ROOT)", "SC_COMPILER_ENABLE_CONFIG=1"});
project.addIncludePaths({
".",
"Tests/SCTest",
});
addSaneCppLibraries(project, parameters);
project.addFiles("Tests/SCTest", "*.cpp");
project.addFiles("Tests/SCTest", "*.h");
project.addFiles("Tests/Libraries", "**.c*");
project.addFiles("Tests/Libraries", "**.inl");
project.addFiles("Tests/LibrariesExtra", "**.cpp");
project.addFiles("Tests/Support", "**.cpp");
project.addFiles("Tests/Tools", "**.cpp");
project.addFiles("Tools", "SC-*.cpp");
project.addFiles("Tools", "*.h");
SourceFiles specificFiles;
specificFiles.addSelection("Tests/SCTest", "*.cpp");
specificFiles.removeSelection("Tests/SCTest", "SCTest.cpp");
specificFiles.compile.addDefines({"SC_SPACES_SPECIFIC_DEFINE=1"});
specificFiles.compile.addIncludePaths({"../Directory With Spaces"});
specificFiles.compile.disableWarnings({4100});
specificFiles.compile.disableWarnings({"unused-parameter"});
specificFiles.compile.disableClangWarnings({"reserved-user-defined-literal"});
project.addSpecificFileFlags(specificFiles);
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 imgui;
SC_TRY(Tools::installDearImGui(parameters.directories, imgui));
project.addIncludePaths({".", sokol.packageLocalDirectory.view(), imgui.packageLocalDirectory.view()});
addSaneCppLibraries(project, parameters);
project.addFiles(imgui.packageLocalDirectory.view(), "*.cpp");
project.addFiles(sokol.packageLocalDirectory.view(), "*.h");
String imguiRelative, imguiDefine;
Path::AsNative));
SC_TRY(StringBuilder(imguiDefine).format(
"SC_IMGUI_PATH=$(PROJECT_ROOT)/{}", imguiRelative));
project.addDefines({"SC_LIBRARY_PATH=$(PROJECT_ROOT)", imguiDefine.view()});
if (parameters.platform == Platform::Apple)
{
project.addFiles("Examples/SCExample", "*.m");
project.addLinkFrameworks({"Metal", "MetalKit", "QuartzCore"});
project.addLinkFrameworksMacOS({"Cocoa"});
project.addLinkFrameworksIOS({"UIKit", "Foundation"});
}
else
{
project.addFiles("Examples/SCExample", "*.c");
if (parameters.platform == Platform::Linux)
{
project.addLinkLibraries({"GL", "EGL", "X11", "Xi", "Xcursor"});
}
}
if (parameters.platform == Platform::Windows)
{
project.addDefines({"IMGUI_API=__declspec( dllexport )"});
}
else
{
project.addDefines({"IMGUI_API=__attribute__((visibility(\"default\")))"});
}
project.addFiles("Examples/SCExample", "**.h");
project.addFiles("Examples/SCExample", "**.cpp");
return Result(true);
}
Result buildSingleFileLibs(Definition& definition, const Parameters& parameters)
{
Workspace workspace = {"SCSingleFileLibs"};
FileSystemIterator fsi;
FileSystemIterator::FolderState entries[1];
String path;
SC_TRY(
Path::join(path, {parameters.directories.libraryDirectory.view(),
"_Build",
"_SingleFileLibrariesTest"}));
SC_TRY_MSG(fsi.init(path.view(), entries),
"Cannot access _Build/_SingleFileLibrariesTest");
while (fsi.enumerateNext())
{
StringView name, extension;
if (extension != "cpp" or not name.startsWith("Test_"))
continue;
Project project;
project.name = name;
project.targetName = project.name;
project.setRootDirectory(parameters.directories.libraryDirectory.view());
project.addDefines({"SC_COMPILER_ENABLE_STD_CPP=1"});
project.configurations[0].compile.enableStdCpp = true;
project.configurations[1].compile.enableStdCpp = true;
project.addIncludePaths({"_Build/_SingleFileLibraries"});
project.addFile(fsi.get().path);
if (parameters.platform == Platform::Apple)
{
project.addLinkFrameworks({"CoreFoundation", "CoreServices"});
}
if (parameters.platform != Platform::Windows)
{
project.addLinkLibraries({"dl", "pthread"});
}
workspace.projects.push_back(
move(project));
}
definition.workspaces.push_back(
move(workspace));
return Result(true);
}
static constexpr StringView DEFAULT_WORKSPACE_NAME = "SCWorkspace";
Result configure(Definition& definition, const Parameters& parameters)
{
Workspace workspace = {DEFAULT_WORKSPACE_NAME};
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));
(void)buildSingleFileLibs(definition, parameters);
return Result(true);
}
Result executeAction(const Action& action) { return Build::Action::execute(action, configure, DEFAULT_WORKSPACE_NAME); }
}
}
#define SC_COMPILER_WARNING_POP
Pops warning from inside a macro.
Definition Compiler.h:103
#define SC_COMPILER_WARNING_PUSH_UNUSED_RESULT
Disables unused-result warning (due to ignoring a return value marked as [[nodiscard]])
Definition Compiler.h:138
constexpr T && move(T &value)
Converts an lvalue to an rvalue reference.
Definition Compiler.h:257
#define SC_TRY_MSG(expression, failedMessage)
Checks the value of the given expression and if failed, returns a result with failedMessage to caller...
Definition Result.h:59
#define SC_TRY(expression)
Checks the value of the given expression and if failed, returns this value to caller.
Definition Result.h:48
@ Debug
Compile for debug, enabling ASAN (if not set on project and never on VStudio)
@ Release
Compile for release.
@ DebugCoverage
Compile for debug, enabling coverage (sets ClangCL for VStudio)
@ VisualStudio2022
Generate projects for Visual Studio 2022.
Definition Build.h:103
@ ConsoleExecutable
Create console executable program.
Definition Build.h:320
@ GUIApplication
Create graphical application program.
Definition Build.h:321
static bool relativeFromTo(StringView source, StringView destination, String &output, Type type, Type outputType=AsNative)
Get relative path that appended to source resolves to destination.
static bool parseNameExtension(const StringView input, StringView &name, StringView &extension)
Splits a StringView of type "name.ext" into "name" and "ext".
static bool join(String &output, Span< const StringView > inputs, StringView separator=SeparatorStringView(), bool skipEmpty=false)
Joins multiple StringView with a Separator into an output String.
This is the list of videos that have been recorded showing some of the internal thoughts that have been going into this library:
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.