DEV Community

estrolabz
estrolabz

Posted on • Edited on

Tutorial - How to create your own static library in C++ (WINDOWS)

Introduction

Hello everyone and welcome to my first ever tutorial! In this tutorial I will walk you through how to create your own static library project from scratch in C++ with Premake5.

We will be using premake5 as it allows us to write some lua code to configure our project and then create a build system for any platform we required to develop on. It is very easy to setup and this tutorial will cover it.

This tutorial will be done for windows 11 currently, however future tutorials on different operating systems will be release later on but for now windows is what this tutorial will focus on.

I will provide some references to other operating systems in case you would like to follow along anyways but if you come across issues they will not be directly supported in this tutorial.

Requirements

  • You must already have knowledge of what a static library is and when to use them.
  • Have Visual Studio 2022 installed correctly. (With C++ development addons)
  • Basic knowledge of C++ syntax
  • Using windows (Preferably), linux or macos.
  • Have a basic code editor installed such as vscode, sublime or something else.

Tutorial

Step 01 - Setting up our project directories

So the first thing we need to do is create a folder called whatever our project is for example I will call mine Static_Library_Project_Template .


Next we will need to open this folder in our preferred minimal code editor, for me this is vscode:


Once you have go to this stage we can create a number of folders ready to be used for our project, here is my recommended folder structure:

Static_Library_Project_Template
-> Build - This will be used to hold the premake5 binary executable.
-> Scripts - This will be used to hold the scripts to run the premake build 
   system creation for each platform (Windows, Linux, MacOS).
-> Libs - Holds all libraries in our project/solution
-> Apps - Holds all application in our project/solution
Enter fullscreen mode Exit fullscreen mode

Step 02 - Setting Up our Base Configuration Files

Next we can create the base files in our root project directory:

Static_Library_Project_Template
-> Build-Solution.lua - This file is the premake5 configuration file for 
   creating our project buld system.
-> .gitignore - Include this file only if you are using git
-> README.md - Include this file only if you are using git
Enter fullscreen mode Exit fullscreen mode

Lets go ahead and start writing our configuration file in lua, so to do this open your Build-Solution.lua file and write the following code:

-- Define a new workspace named "PROJECT_NAME"
workspace "PROJECT_NAME"

    -- Set the target architecture to 64-bit
    architecture "x64"

    -- Define 3 build configurations: DEBUG, RELEASE, and DIST (distribution)
    configurations { "DEBUG", "RELEASE", "DIST" }

    -- Specify the default startup project when the solution is opened
    startproject "Apps/DebuggerApp"

    -- Apply the following settings **only on Windows systems**
    filter "system:windows"
        -- Enable C++ exception handling using standard C++ semantics
        buildoptions { "/EHsc",
            -- Enforce use of standard conforming preprocessor
                       "/Zc:preprocessor",
            -- Ensure __cplusplus macro reflects the actual C++ language version
                       "/Zc:__cplusplus" }

-- Define a variable that sets the output directory pattern based on system, architecture, and config
OutputDir = "%{cfg.system}-%{cfg.architecture}/%{cfg.buildcfg}"

-- Start a new group in the solution explorer (grouping related projects)
group ""
    -- Include a Lua build script for a static library called StaticLibName
    include "Libs/StaticLibName/Build-StaticLibName.lua"

-- Return to root group (no name)
group ""

-- Include the build script for the DebuggerApp application
include "Apps/DebuggerApp/Build-DebuggerApp.lua"
Enter fullscreen mode Exit fullscreen mode

So with this please not you can change the PROJECT_NAME to what your project is called. You can also change the StaticLibName to your library name. If you would also like you can change the DebuggerApp to your own custom name however we called it this as it will only be used for testing our library works which is the reason we are creating an Executable application in our library project.


So far you should have something like this:


Step 03 - Next we need to install and setup premake5.exe

We can download premake5.exe from the following link:

https://premake.github.io/download

Make sure that you download the version that is for your operating system.


Once downloaded we need to copy/move the premake5.exe file into our Build directory.

I have downloaded the file and it gives me a zipped executable file, so I will now unzip this and I will have the following:

Then we go into the extracted folder and find our executable:

This is the file we must copy / move into our Build directory in our project folder so lets go ahead and do that:

As you can see I have now moved this file into the correct folder and this is also what you should have too.


Step 04 - Windows Pre-Build Script

In this step we need to create the windows batch script that will run premake5.exe with our configuration to create our development environment based on our custom configuration.


Lets start with creating a file called Setup-Win64.bat in the Scripts Folder:


Now lets write some batch script code:

@echo off
REM Disable command echoing for cleaner output

pushd ..
REM Save current directory and change to parent directory

Build\premake5.exe --file=Build-Solution.lua vs2022
REM Run Premake5 to generate Visual Studio 2022 project files using Build-Solution.lua

popd
REM Return to the original directory
Enter fullscreen mode Exit fullscreen mode

Perfect now you should have something that looks like this:


This is great but if we were to run this batch script now we would have an error: why is that? Well you see although we created the configuration file we actually need to create our static library project and Debugger App project with a configuration file in each of these projects for this to all work.

Step 05 - Create the Static Library Project

So lets start by creating a folder in our Libs folder called whatever it is we named our static library in our Build-Solution Configuration File.

For me that is StaticLibName.

As you can see I have created the folder called StaticLibName inside of the Libs folder.


Next we need to create another configuration file in lua called Build-StaticLibName.lua . And then we must make sure we have the following code:

-- Define a new project named "StaticLibName"
project "StaticLibName"

   -- Set the project type to a static library (.lib)
   kind "StaticLib"

   -- Set the programming language to C++
   language "C++"

   -- Use the C++23 standard for compilation
   cppdialect "C++23"

   -- (Temporary) set the output directory for binaries based on build config (e.g., DEBUG/RELEASE)
   targetdir "Binaries/%{cfg.buildcfg}"

   -- Use the shared C runtime (not statically linked)
   staticruntime "off"

   -- Add all header (.hpp) and source (.cpp) files from these folders to the project
   files { "Includes/**.hpp", "Source/**.cpp" }

   -- Include the "Includes" folder so headers can be found during compilation
   includedirs
   {
      "Includes"
   }

   -- Set final target output path using system, architecture, config, and project name
   targetdir ("../../Binaries/" .. OutputDir .. "/%{prj.name}")

   -- Set intermediate object files output path for compiled objects
   objdir ("../../Binaries/Intermediates/" .. OutputDir .. "/%{prj.name}")

   -- Apply settings only for Windows systems
   filter "system:windows"
       -- Use the latest available Windows SDK version
       systemversion "latest"

       -- Define a macro indicating the build is for 64-bit Windows
       defines { "DEF_WIN64" }

   -- Apply settings for the DEBUG configuration
   filter "configurations:DEBUG"
       -- Define a macro for debug builds
       defines { "DEF_DEBUG" }

       -- Link the debug version of the runtime library
       runtime "Debug"

       -- Enable debug symbols (for stepping through code in debugger)
       symbols "On"

   -- Apply settings for the RELEASE configuration
   filter "configurations:RELEASE"
       -- Define a macro for release builds
       defines { "DEF_RELEASE" }

       -- Link the release version of the runtime library
       runtime "Release"

       -- Enable compiler optimizations
       optimize "On"

       -- Keep debug symbols enabled (optional, for profiling/symbolic stack traces)
       symbols "On"

   -- Apply settings for the DIST (distribution/final product) configuration
   filter "configurations:DIST"
       -- Define a macro for final distribution builds
       defines { "DEF_DIST" }

       -- Use release runtime
       runtime "Release"

       -- Enable compiler optimizations
       optimize "On"

       -- Disable debug symbols to reduce binary size and hide internals
       symbols "Off"

Enter fullscreen mode Exit fullscreen mode

Now we have this configuration file lets write some sample code for our library:

Firstly lets create two folders in the StaticLibName folder:

StaticLibName
-> Includes
-> Source
Enter fullscreen mode Exit fullscreen mode

After that lets create two files a .cpp and .hpp file now the .hpp file will go inside the Includes folder and the .cpp file will go inside the source folder. The file should be named Add.hpp and Add.cpp .

Add.hpp

namespace MATH {
    int add(int a, int b);
}
Enter fullscreen mode Exit fullscreen mode

Add.cpp

#include "Add.hpp"

int MATH::add(int a, int b) {
    return a + b;
}
Enter fullscreen mode Exit fullscreen mode

Step 06 - Create the Debugger App Project

So lets start by creating a folder in our Apps folder called whatever it is we named our Debugger App in our Build-Solution Configuration File.

For me that is DebuggerApp.

As you can see I have created the folder called DebuggerApp inside of the Apps folder.


Next we need to create another configuration file in lua called Build-DebuggerApp.lua . And then we must make sure we have the following code:

-- Define a new project named "DebuggerApp"
project "DebuggerApp"

    -- Set the project type to a console application (produces .exe)
    kind "ConsoleApp"

    -- Use the C++ programming language
    language "C++"

    -- Use the C++23 standard for compiling the code
    cppdialect "C++23"

    -- Set a temporary target output directory based on build configuration
    targetdir "Binaries/%{cfg.buildcfg}"

    -- Link against the dynamic C runtime (instead of statically)
    staticruntime "off"

    -- Add all header (.hpp) and source (.cpp) files from these folders to the project
    files { "Includes/**.hpp", "Source/**.cpp" }

    -- Specify include directories for header file lookup during compilation
    includedirs
    {
        "Includes",                            -- Local project headers
        "../../Libs/StaticLibName/Includes"    -- Headers from the linked static library
    }

    -- Link this app against the "StaticLibName" static library
    links
    {
        "StaticLibName"
    }

    -- Set the final output directory for the executable (organized by OS, arch, config, and project)
    targetdir ("../../Binaries/"..OutputDir.."/%{prj.name}")

    -- Set the intermediate directory for object files during compilation
    objdir ("../../Binaries/Intermediates/"..OutputDir.."/%{prj.name}")

    -- Apply the following settings only when building on Windows
    filter "system:windows"
       -- Use the latest available Windows SDK
       systemversion "latest"

       -- Define a macro to indicate the build is for 64-bit Windows
       defines { "DEF_WIN64" }

    -- Configuration-specific settings for DEBUG
    filter "configurations:DEBUG"
       -- Define a macro to indicate a debug build
       defines { "DEF_DEBUG" }

       -- Use the debug runtime library
       runtime "Debug"

       -- Enable debug symbols for debugging in IDEs
       symbols "On"

    -- Configuration-specific settings for RELEASE
    filter "configurations:RELEASE"
       -- Define a macro to indicate a release build
       defines { "DEF_RELEASE" }

       -- Use the release runtime library
       runtime "Release"

       -- Enable optimizations for performance
       optimize "On"

       -- Keep debug symbols (optional for debugging or profiling)
       symbols "On"

    -- Configuration-specific settings for DIST (distribution/final release)
    filter "configurations:DIST"
       -- Define a macro to indicate a distribution build
       defines { "DEF_DIST" }

       -- Use the release runtime library
       runtime "Release"

       -- Enable full compiler optimizations
       optimize "On"

       -- Disable debug symbols to reduce binary size and improve load time
       symbols "Off"

Enter fullscreen mode Exit fullscreen mode

Now we have this configuration file lets write some sample code for our library:

Firstly lets create two folders in the DebuggerApp folder:

DebuggerApp
-> Includes
-> Source
Enter fullscreen mode Exit fullscreen mode

After that lets create two files a .cpp and .hpp file, now the .hpp file will go inside the Includes folder and the .cpp file will go inside the source folder. The files should be named Test.hpp and Main.cpp .

Test.hpp

#include <iostream>

void log_test(int res) {
    std::cout << res << std::endl;
}
Enter fullscreen mode Exit fullscreen mode

Main.cpp

#include "Test.hpp"
#include <Add.hpp>

int main() {
    int result = MATH::add(5, 6);
    log_test(result);
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Step 07 - Running our Pre-Build Setup and Testing

So now that we have set everything up go to a terminal inside the Scripts folder like this:


And type the following command:

.\Setup-Win64.bat
Enter fullscreen mode Exit fullscreen mode

Press enter and you should see this:


Perfect now you can open the PROJECT_NAME.sln file and it should open this window:


We can now press the Local Window Debugger button with a big green arrow before it to run our solutions and see what happens.

Here is what you should get:


SUCCESS

If you get this then well done you have SUCCESSFULLY completed this tutorial and setup your first project with a static library. :)

Contact Information

https://www.instagram.com/estrolabz/

https://www.patreon.com/c/ESTROLABZ

https://www.youtube.com/@estrolabzuk

https://github.com/estrolabz

https://dev.to/estrolabz

https://medium.com/@estrolabz

https://www.reddit.com/r/ESTROLABZ/

https://autumn-oboe-c16.notion.site/ESTROLABZ-Blog-237763e1ef908090a5fec3ea8c04331e?source=copy_link

Top comments (0)