Creating Projects with Irrlicht and Premake

~ Table of Contents ~

  1. Introduction
  2. What is Premake?
  3. Initial Steps
    1. File Format
    2. Your Project File Structure
    3. Creating Needed Objects
  4. A Little About Lua
  5. Setting up a Premake Project
  6. Setting up Irrlicht
  7. Putting It All Together
  8. Additional Notes

You are free to copy, modify (if necessary for maintenance), and share this article as much as you wish, but I make no guarantee of the accuracy of anything herein. This clause is primarily intended for wikis.

~ Introduction ~

Irrlicht is a free, zlib-style-licensed 3D engine, having its own built-in engines as well as acting as a wrapper for OpenGL and DirectX. It is tailored primarily towards speed (and thus, gaming) and is relatively easy to modify.

If you’re a new C or C++ programmer, you may have heard of “Make” and “CMake”. Make is a program on Linux distros that sends commands to the compiler for how to.. well, make a program executable. CMake is supposedly the cross-platform version of “make” in that it creates make files for different operating systems. However, I’ve heard complaints that it’s a mess, and it just adds to the work. In steps Premake…

~ What is Premake ~

Premake is a free, small, cross-platform program that creates g++ make and Visual Studio project files. Premake, unlike CMake, does not try to take control of your project. Instead, by creating these project files, it allows you to work optimally for the OS you’re on. Premake uses lua for allowing you to type in a simple syntax for setting up a project file. Then, once you’ve set up the file, you can run in the command line or terminal:

  • For gcc/g++: premake gmake
  • For Visual Studio [year]: premake vs[year]

NOTE: You need to have the current working directory for the command prompt or the terminal to be the folder containing premake.lua (or premake4.lua, as I’ll mention in a moment).

IMPORTANT NOTE: At the current time, version 5 of premake is still in its development phase, and it is recommended that you use premake4, which requires you to type “premake4” rather than “premake”.

On Linux Ubuntu distros, you can get premake 4 by typing “apt-get install premake4” into your terminal.

~ Initial Steps ~

~ A) File Format ~

For cross-platform capability (between Windows and Linux), it is very important (unless you want a headache) that your header files (files ending in “.h”) and c++ files (files ending in “.cpp”) or c files use Linux newline character endings and NOT return character endings. Linux files use the newline character (C++ “\n”), Mac uses the return character (C++ “\r”), and Windows uses BOTH (C++ “\r\n”). The compiler with Visual Studio can process both, but GCC (the compiler on linux) will only gripe about some loose character. Also, after you write code with Windows, it is important to remove the “smart quotes”, which Linux can’t read either. You can use a program like Notepad++ to open and save the files, and maybe then the issue will go away, but I’ll mention in a second other methods.

In short, the errors you will get from gcc include “stray \377”, “stray \342”, and the like.

To remove these on Linux, there are a number of ways, but one of them alone doesn’t seem to cut it, so I’ll mention all of them.

First, try a program called “tofrodos”. You can get it via “apt-get install tofrodos”, but to you it, you can’t use the command “tofrodos”. Instead, use “fromdos” (i.e. from dos) with the option “-o” to overwrite the original files. For example:

fromdos -o main.cpp

There is more about line ending character conversions on a someone else’s blog post. This only converts line endings, really, but even then it might mess up.

Another program you can use is “sed”, which you can use to batch convert files and eliminate smart quotes and giving the terminal the following command:

for i in *.txt; do sed -i ".h" s/[”“]/'"'/g $i; done

Noting that “.h” can be replaced with “.cpp” for the C++ files. More information is available on the blog post where I found out about it.

One last thing for Linux users: If, when you try to compile your program, the compiler is still complaining about stray characters or missing newlines or some error related to lines, open the offending file in gedit and turn on line numbers via: Edit > Preferences > Line Numbers > Display Line Numbers. Then scan the file until you see lines that appear to wrap around even when no text is on them. For example:

14 | int main() {
15 | return 0;
|
16 | }

Obviously, line 15 shouldn’t have a blank line below it. Erase that blank line.

~ B) Your Project File Structure ~

To use Premake4, add the file “premake4.lua” to your project directory. The rest of this article will assume you have such a file located there.

The file system I will be assuming you have is this:

– projects (folder of any name)

— my Project
— /premake4.lua
— /src
—- /src/main.cpp
— /rsrc
—- /rsrc/image.png

— my Other Project
— /src
— /src/other.h
— /src/other.cpp

I will be referencing “my Project” and “my Other Project” as the folders for the current project and the other project respectively, meaning that the relative path from our premake4.lua file to the other.h file will be: “../myOtherProject/src/other.h”. This should simplify my examples, even though, frankly, my real project folders tend to be buried in more folders.

~ C) Creating Needed Objects ~

You may have header or cpp files that you wish to use for multiple projects. For Windows, you can include these files in the project the same way you would your regular files: Right click on a filter in the projects panel and select “Add Existing Item” from the drop-down menu. But if you are using Premake, hopefully these files can be added for you using the “files” command (more on that later).

GCC users will need to create object files. To create an object file, go to the location of the source code file, right click on the folder and select “Open in Terminal”. Using the example “my Other Project”, we can compile its code without linking by going to the folder “my Other Project”/src and typing in the following command in the terminal:

g++ -c other.h other.cpp

Naturally, of course, multiple files can be included. The result of my example output will be a single object file called “other.o”. This file is important.

~ A Little About Lua ~

Lua is a scripting language that looks more like a bunch of command prompt entries than any typical language. It is fairly straightforward to learn, and there are good, free manuals available online. For now, I will cover the basics that you need to know for this article (and thus making projects in general).

  • An expression in Lua is deliminated/ended by a newline. (Same as Python)
  • A variable is declared by assigning something to an unused name.
  • All variables are considered global unless their name is preceded by the word “local” when they are created.
  • Numbers do not have a type identifier (there is no “float”, “int”, etc).
  • Dependent sequences of events must be wrapped in a “do”…”end” execution block. So for example:
    local a = 5
    do
    local b = a + 2
    end
  • Strings are enclosed in quotes and can be concatenated/combined by putting two periods (“..”) between them.
  • Lua uses tables instead of arrays. Tables are like mapped arrays. The index starts at 1 rather than 0. They are created by enclosing a list of objects, separated by commas, in curly brackets. For example:
    { “idx1”, 2, “me third” }
  • Comments are created by beginning a line with tow dashes (“–“). Long comments begin with two dashes and an open square bracket (“–[“) and end with a closing square bracket (“]”). A neat trick is to precede the closing square bracket with two dashes so that, when the comment contains code that should now be used, the first square bracket can be preceded by three dashes, effectively making both it and its closing bracket one line comments with a single key stroke.
  • Strings can be created with either single quotes (‘) or double qoutes (“), allowing you to embed one type of quote within another in the same string. For example:
    ‘This is a “quoted” quote.’

Lua doesn’t appear to be dependent on tabbing, but it is easier to read with tabbing.

You will not need to know about any special libraries or commands in Lua for basic programs. For Premake commands, there is a scripting reference, but I’ll cover what you need later in this article.

Notepad++ and GEdit both support syntax highlighting for Lua, and of course Notepad++ will allow you to create your own language for syntax highlighting, to which you can add the Premake commands so that they are highlighted or appear commented out, etcetera.

~ C) b) Premake Lua ~

Premake has a list of essential commands for setting up projects.

  • solution – Sets up a solution. This command accepts a single string parameter.
  • project – Sets up a project (within a solution). This command accepts a single string parameter.
  • buildoptions – Direct commands for the build-stage of the compiler (and with this and the linker commands, you could do everything you ever needed to, though that would be hard to maintain). This command accepts a table parameter that contains strings that could be directly passed to the compiler.
  • linkoptions – Direct commands for the linking-stage of the compilation, i.e. commands to the “linker”. This command accepts a table parameter that contains strings that could be directly passed to the linker. (Later, we will use this to pass object files.)
  • language – Sets the language for the compiler. If none is specified, the compiler will have to determine it. This command accepts a string, such as “C++”.
  • kind – Sets the project kind (primarily for Visual Studio). This command accepts a string of one of four string values: ConsoleApp (all Linux programs will use this), WindowedApp (for Windows and MacOS), StaticLib, SharedLib.
  • links – A list of libraries to link to. This command accepts a table of strings with direct library names, not the file names of libraries. So, for example, irrlicht will be called “Irrlicht”. No extension is needed except on Mac.
  • files – A list of files to directly include in the project. This command accepts a table parameter that contains all of the source file paths that are relative file paths (relative to the location of the premake4.lua project file). You can wildcard include header files in a directory by using “*.h” instead of the file name, and likewise you can wildcard include c++ files in a directory by using “*.cpp” instead of a filename. You can wildcard include header files in a directory and its sub-directories with “**.h”, and similarly, “**.cpp”.
  • excludes – A list of files to exclude (even if included with wildcards).
  • defines – Creates a “#define” for the precompiler. This can be set to a value by following the defined name with an equals sign and the value, e.g. “_ME_DEFINED=1”.
  • configuration – Sets up a specific build configurations. Note that this is supposed to be replaced by the command “filter” in future versions. You can use the configuration in the terminal with “make config=[config]” where “[config]” might be “debug”, “release”, or whatever configuration you have set up.
  • objdir – Sets the directory where the output object files will be located. This command accepts a path relative to the premake4.lua file.
  • targetdir – Sets the directory where the output executable file will be located. This command accepts a path relative to the premake4.lua file.

Note that these are commands, so parentheses are not necessarily required except when passing values by variable.

A little hint about Premake:

As it turns out, much of what Premake does for Makefiles is merely copying and pasting what you put into Premake. In some ways, it might be just as fast to write out a Makefile, but then you wouldn’t be able to easily create Visual Studio projects. However, this nearly perfect pasting mechanism of Premake allows us to abuse it in some regards, although notably Premake provides buildoptions and linkoptions as a more direct means to communicate with the compiler.

~ Setting up a Premake Project ~

I will be using Premake4 for the remainder of this tutorial, so references may not apply to older or newer versions of the program.

The Premake4 project setup requires a premake4.lua, and since Premake4 uses relative paths for files (except library files), it would be annoying to have it anywhere else. As I said, this premake4.lua file should be located in your project directory just below the src folder.

The Premake4 project file structure I’m going to set up will have this form:

  • variables for file paths
  • solution
    • projects
      • build information
      • linking information
      • configurations

Configurations could themselves contain the build and linking information, but I don’t want to complicate the tutorial yet. Just know that anything that can be put under “projects” could be nested further in the configuration.

Let’s start with a basic file:

======== premake4.lua ========

— Variables section. I will refer to this later.

local v_lang = “C++”

— Identify the platform for our configuration
local v_platform = iif( os.is(“windows”), “windows”, “linux” )

— Path to the source file of the other project.
local v_other_proj_path = ‘../”my Other Project”/src/’

— Path to my library (an absolute path)
local v_my_library = ‘usr/local/lib’

— Solution setup
–[[ Every project must have this (it is really just a carry-over from Visual Studio since g++ obviously needs no solution ]]
solution “My Solution”
project “My Project”
lang ( v_lang )
kind “ConsoleApp”
links { “myLibrary” }
files { “main.cpp”, v_other_proj_path .. “other.h”, v_other_proj_path .. “other.cpp” }

— Build information
buildoptions { ” -I” .. v_other_proj_path }

— Linker information
linkoptions { ” other.o”, ” -L” .. v_my_library }

— Configuration information
configuration “Debug”
defines { “DEBUG” } — has same effect as cpp file #define DEBUG
objdir “obj/debug”
targetdir “bin/debug”

configuration “Release”
defines { “NDEBUG” }
objdir “obj/release”
targetdir “bin/release”

========================

~ Setting up Irrlicht ~

Before you use Irrlicht, there are a couple of steps to building an installing it. If you do not want to install Irrlicht, you will need to use the premake command “libdirs” and pass it a path to the folder containing your library. Visual Studio will convert this to an absolute path (unfortunately), making sharing the project more difficult.

Installing Irrlicht is now easy for both Windows and Linux. For Windows users, download the latest version, and copy the Irrlicht.dll file from the bin/Win32 folder to your computer’s System folder.

For Linux users, there are three steps:

  1. Run the Makefile in the Irrlicht source folder.
  2. Use the install.sh file (alternatively, you can copy the header files into usr/local/include/irrlicht and copy the libIrrlicht.so file from lib/Linux folder into usr/local/lib).
  3. Run “sbin/ldconfig” in the terminal.

There is a tutorial on the main irrlicht wiki for all of this, including the setup for MacOS. The tutorial is slightly outdated because, at the time, there was no install.sh file for Linux users.

The tutorial does have an important bit of information:

For developing with Irrlicht, certain developer libraries are required. You can obtain these with the terminal command:

sudo apt-get -y install build-essential xserver-xorg-dev x11proto-xf86vidmode-dev libxxf86vm-dev mesa-common-dev libgl1-mesa-dev libglu1-mesa-dev libxext-dev libxcursor-dev

Notably, this should eliminate the missing Xcursor error.

~ Putting It All Together ~

Once Irrlicht is all set up and installed, the next step is modifying the premake project file (premake4.lua) to include the Irrlicht engine.

Please note that names are case-sensitive to a compiler. There is a difference between “Xcursor” and “XCursor” and “xcursor”. Hence, if the compiler starts reporting missing libraries, please double check your spelling.

In the premake file variables section, add the following:

==============

local v_irrlicht_home = “usr/local”
local v_irrlicht_include = “usr/local”
do
if ( os.is(“windows” ) then
v_irrlicht_home = “path-to-your-irrlicht-folder (not source folder)”
v_irrlicht_include = v_irrlicht_home .. “/include”
end
end

==============

That was simply saving paths to variables so we don’t have to write them out repeatedly.

The next step is adding Irrlicht to the links. If you want to take into account both Windows and Linux, then you need to modify the projects section of the premake file a bit. Here’s what it might look like:

==============

project “My Project”
lang ( v_lang )
if ( os.is(“windows”) ) then
kind “WindowedApp”
links { “Irrlicht” }
else — assume Linux
kind “ConsoleApp”
links { “Irrlicht”, “GL”, “Xxf86vm”, “Xext”, “X11”, “Xcursor” }
end

==============

Now you need to go to the build options and add another string to that table:

“-I”..v_irrlicht_include

The linkoptions also needs to be modified by adding another string, but this is little more work if you want both Windows and Linux:

==============

if ( os.is(“Windows” ) then
linkoptions { ” other.o”, ” -L” .. v_my_library, ” -L” .. v_irrlicht_home .. “/lib/Win32-visualstudio” }
else
linkoptions { ” other.o”, ” -L” .. v_my_library, ” -L” .. v_irrlicht_home .. “/lib” }
end

==============

Note: I did NOT test out the Windows code, but this is transcribed from the Irrlicht example makefiles. The examples use “$(SYSTEM)” rather than “Win32-visualstudio”, but if you look in the Irrlicht directories, that’s how it’s set up. And of course, it’s fairly straightforward to change the setup in Visual Studio via the Project Properties.

Finally…

To run the premake4.lua file on Linux, go into your project folder, right click and select “Open in Terminal”, then type in “premake4 gmake”. This should result in two things: A makefile named “Makefile” and the project makefile, named something like “My Project.make” (depending, of course, on the name you gave your solution within the premake4.lua file).

Next, enter the command “make” (with no other arguments), and GCC will let you know if there are any bugs. Your binary should be in the directory you set with the targetdir command in the debug configuration. Note that you can add the argument “config=release” (assuming you have a configuration named “release”) to the “make” command to change which configuration “make” uses. (Again, note that premake 5 may have this feature changed.)

~ Additional Notes ~

This article was written over the course of a few days, so I may have missed something, forgotten something, or there might be some other problem. Feel free to leave a note in the comments section.

I have successfully compiled software on Linux with GCC with this method, so I know it works in that regard.

In order to recompile cleanly on Linux, you should run “make clean”, and then recreate the make file using premake.

Enter the space and time of my little world… Welcome Earthling.

Blog at WordPress.com.