Sunday, May 24, 2015

Project generator

I like to experiments and have fun doing that. Right now I work on another crazy idea. You know because developing of game and technology behind is not enough. Don't worry this is just another side project connected with this one. 

Project generator.

I know that I could use some existing solutions (i.e CMake) but this wouldn't be so fun as doing it myself in python :] Well I started doing ... some time ago and returning to it whenever I had some time. Right now I'm at the level where Linux make support is better than previous generator I use. Sadly Microsoft Visual Studio support not exist there right now :D

Well I fix this in future. But lets move to some details about new project generator which is wrote in python.



Reason behind new system ?
There were few:

  • Support of old C++ generator was painful.
  • Linus support shown flaws of old generator.
  • I wanted more readable format of configurations.
  • I wanted more control over data.
  • I wanted shorter iteration time.
  • I wanted to test some crazy idea how to approach problem.


How it work ?
This is funny part. I execute python script which:

  1. Convert solution config file to python script
  2. Convert configs files to python scripts
  3. Execute this generated scripts to gather configuration data
  4. Create output result.
And some implementation details

New format is simple. Solution file contain just info where is root folder is and projects in groups:
set rootdir = "../../"
          // ... other groups
Lba        = [ "source/LbaGame/LbaGame.proj",  "source/LbaExec/LbaExec.proj" ]
and convert it to:
########################################################
# Source: 
../../scripts/project_gen/config/Projects.cfg
# Date:2015-05-24 20:21:29.491290
########################################################

import copy
rootDir = "."
def runExport(exporter, values, filter):
rootdir="../../" 
    // ... other groups
    if filter == None or filter == "" or filter == "Lba":
print("Generating: source/LbaGame/LbaGame.proj")
import genLbaGame
localValues = copy.deepcopy(values)
exporter.begin("source/LbaGame/LbaGame.proj", localValues)
genLbaGame.genGenerate(exporter, localValues)
exporter.end(localValues)

print("Generating: source/LbaExec/LbaExec.proj")
import genLbaExec
localValues = copy.deepcopy(values)
exporter.begin("source/LbaExec/LbaExec.proj", localValues)
genLbaExec.genGenerate(exporter, localValues)
exporter.end(localValues)

Project file is also really simple:
#include "General.proj.cfg" 
def wrCore:
{
    set ProjectName = "wrCore"
    set Guid              = "6B289599-C65E-47DE-A3E2-F11F82B4CEE9"
    set Namespace   = "wre"
    set Keyword       = "Win32Proj"
    Files = ["wrCore_pch.h", "wrCore_pch.cpp", "include/*", "src/*"]
    References = ["source/wrBase/wrBase.proj","source/wrFileSystem/wrFileySystem.proj", "source/wrTools/wrTools.proj", "source/wrPlatformPC/wrPlatformPC.proj"]
def Generate:
{
    export("Globals", [Default, wrCore])
    if System == 'Windows':
    {
        export ("Win32|Lib|Debug",   [Default, Windows32, Debug,   Lib,  wrCore])
        // ... other configurations
    }
    if System == 'Linux':
    {
        export ("Linux32|Lib|Debug",   [Default, Linux32, Debug,   Lib,  wrCore])
        // ... other configurations
    }
}
and convert it to:
########################################################
# Source:../../source/wrCore/wrCore.proj
# Date:2015-05-24 20:21:29.583295
########################################################

import copy
#include "General.proj.cfg"
from genGeneral import
def genwrCore(exporter, values):
values['ProjectName']="wrCore"
values['Guid']="6B289599-C65E-47DE-A3E2-F11F82B4CEE9"
values['Namespace']="wre"
values['Keyword']="Win32Proj"
values['Files']=["wrCore_pch.h" ,"wrCore_pch.cpp" ,"include/*" ,"src/*"]
values['References']=["source/wrBase/wrBase.proj" ,"source/wrFileSystem/wrFileySystem.proj" ,"source/wrTools/wrTools.proj" ,"source/wrPlatformPC/wrPlatformPC.proj"]
def genGenerate(exporter, values):
localValues = copy.deepcopy(values)
genDefault(exporter, localValues)
genwrCore(exporter, localValues)
exporter.addConfig("Globals", localValues)
if values['System'] == 'Windows' :
localValues = copy.deepcopy(values)
genDefault(exporter, localValues)
genWindows32(exporter, localValues)
genDebug(exporter, localValues)
genLib(exporter, localValues)
genwrCore(exporter, localValues)
exporter.addConfig("Win32|Lib|Debug", localValues)
         // ... other configurations
if values['System'] == 'Linux' :
localValues = copy.deepcopy(values)
genDefault(exporter, localValues)
genLinux32(exporter, localValues)
genDebug(exporter, localValues)
genLib(exporter, localValues)
genwrCore(exporter, localValues)
exporter.addConfig("Linux32|Lib|Debug", localValues)
         // ... other configurations
Summary 

I know that this may look crazy and probably is. But to say the truth I like it this way till now this setup show ma a lot of potential and I want to use it to fullest.

Of course this is still some kind of proof of concept and there are stuff I want to improve:

  • Better error reporting.
  • Some better organisation of  python code.
  • Better integration with my build system.
  • Visual Studio support 
And probably hundreds of other things that will show in meantime. 

For now I'm really happy with  results that this approach gave me. What you think about this method ? I'm really curious to hear your opinion.

Greg


No comments:

Post a Comment