Skip to content

SCons:Example

SCons의 SConstruct파일 샘플.

Worldit

SConstruct file

#!/bin/python
# -*- coding: utf-8 -*-

import os
import sys
import copy
import glob
import fnmatch
import re
import library

from os.path import expanduser

HOME_DIR=expanduser('~')
LOCAL_DIR=os.path.join(HOME_DIR, '.local')
LOCAL_INC_DIR=os.path.join(LOCAL_DIR, 'include')
LOCAL_LIB_DIR=os.path.join(LOCAL_DIR, 'lib')

DEBUG_PARAM_NAME = 'debug'
DEBUG_FLAG = '-g'

SOURCE_SUFFIX_LIST = ['*.c', '*.cc', '*.cpp']
OBJECT_SUFFIX = '.o'


def existAndMkdir(dir):
    if os.path.isdir(dir) == False:
        os.mkdir(dir)
    pass

def which(program):
    path = os.getenv('PATH')
    for cursor in path.split(os.path.pathsep):
        cursor = os.path.join(cursor, program)
        if os.path.exists(cursor) and os.access(cursor, os.X_OK):
            return cursor
    return ''

def toList(value):
    if isinstance(value, list):
        return value
    else:
        return [value]
    pass


class Config:

    def __init__(self):
        self.debug = False
        self.windows = False
        self.posix = False

        # Platform example:
        # - MacOSX: darwin
        # - Linux: linux2
        # - Windows: win32
        self.platform = sys.platform

        if int(ARGUMENTS.get(DEBUG_PARAM_NAME, 0)):
            self.debug = True
        if os.name.upper() == 'NT':
            self.windows = True
        if os.name.upper() == 'POSIX':
            self.posix = True
        pass

    def isWindows(self):
        if self.platform == 'win32':
            return True
        else:
            return False

    def isLinux(self):
        if self.platform == 'linux2':
            return True
        else:
            return False

    def isMacOSX(self):
        if self.platform == 'darwin':
            return True
        else:
            return False

    def getInformation(self):
        return {  'Debug'    : self.debug
                , 'Windows'  : self.windows
                , 'POSIX'    : self.posix
                , 'PLATFORM' : self.platform}


class Sources:

    def __init__(self):
        self.sources = []

    def append(self, source):
        self.sources.append(source)

    def appendFromDirectory(self, directory):
        for dirpath, dirnames, filenames in os.walk(directory):
            for ext in SOURCE_SUFFIX_LIST:
                for filename in fnmatch.filter(filenames, ext):
                    self.sources.append(os.path.join(dirpath, filename))
        pass

    def removeMatch(self, match):
        for cursor in self.sources:
            if re.match(match, cursor):
                self.sources.remove(cursor)
        pass


class Compiler:

    def __init__(self, name, debug, sources, toolname = ''):
        self.name = name
        if debug:
            self.name += DEBUG_FLAG

        if toolname:
            self.env = Environment(ENV = os.environ, tools = [toolname])
        else:
            self.env = Environment(ENV = os.environ)

        if debug:
            self.appendAllCflag(DEBUG_FLAG)
            self.appendAllDefine('DEBUG')
        else:
            self.appendAllDefine('NDEBUG')
            self.appendAllDefine('RELEASE')

        self.objects = Compiler.createObjects(self.env, self.name, sources)
        pass

    @staticmethod
    def getObjectName(source, suffix):
        dot_index = source.rfind('.')
        if suffix:
            return source[:dot_index] + '.' + suffix + OBJECT_SUFFIX
        else:
            return source[:dot_index] + OBJECT_SUFFIX
        pass

    @staticmethod
    def createObjects(env, name, sources):
        objects = []
        for cursor in sources:
            objects += env.Object(Compiler.getObjectName(cursor, name), cursor)
        return objects

    def appendSource(self, sources):
        self.objects += Compiler.createObjects(self.env, self.name, sources)

    def appendAllDefine(self, define):
        self.env.Append(CPPDEFINES = toList(define))
    def prependAllDefine(object, define):
        self.env.Prepend(CPPDEFINES = toList(define))

    def appendAllCflag(self, flag):
        self.env.Append(CCFLAGS = toList(flag))
    def prependAllCflag(self, flag):
        self.env.Prepend(CCFLAGS = toList(flag))

    def appendAllCxxflag(self, flag):
        self.env.Append(CXXFLAGS = toList(flag))
    def prependAllCxxflag(self, flag):
        self.env.Prepend(CXXFLAGS = toList(flag))

    def appendAllIncludePath(self, path):
        self.env.Append(CPPPATH = toList(path))
    def prependAllIncludePath(self, path):
        self.env.Prepend(CPPPATH = toList(path))


    def parsePkgConfig(self, command):
        self.env.ParseConfig(command)

    def appendLibraryPath(self, path):
        self.env.Append(LIBPATH = toList(path))
    def prependLibraryPath(self, path):
        self.env.Prepend(LIBPATH = toList(path))

    def appendLibrary(self, library):
        self.env.Append(LIBS = toList(library))
    def prependLibrary(self, library):
        self.env.Prepend(LIBS = toList(library))

    def appendLinkflag(self, flag):
        self.env.Append(LINKFLAGS = toList(flag))
    def prependLinkflag(self, flag):
        self.env.Prepend(LINKFLAGS = toList(flag))


    def runProgram(self):
        return self.env.Program(target = self.name, source = self.objects)

    def runSharedLibrary(self):
        return self.env.SharedLibrary(target = self.name, source = self.objects)

    def runLibrary(self):
        return self.env.Library(target = self.name, source = self.objects)


## -----------------
## Utility function.
## -----------------

def getDefaultCompiler(name, libs):
    config = Config()
    sources = Sources()
    sources.appendFromDirectory('src/' + name)

    if config.windows:
        compiler = Compiler(name, config.debug, sources.sources, 'mingw')
    else:
        compiler = Compiler(name, config.debug, sources.sources)

    if os.path.isfile('property.py'):
        import property
        if os.path.isdir(property.local_dir):
            compiler.appendAllIncludePath(os.path.join(property.local_dir, 'include'))
            compiler.appendLibraryPath(os.path.join(property.local_dir, 'lib'))
        else:
            compiler.appendAllIncludePath(LOCAL_INC_DIR)
            compiler.appendLibraryPath(LOCAL_LIB_DIR)

    if not config.isMacOSX():
        compiler.appendLinkflag('-static-libstdc++')

    compiler.appendAllCxxflag('-std=c++11')
    compiler.appendAllIncludePath('src')
    compiler.appendLibraryPath('.')
    #compiler.appendLibrary(['WS2_32', 'mswsock'])
    #compiler.appendLibrary('atomic')
    compiler.appendLibrary(libs)

    return compiler

def addThirdPartySetting(compiler, path):
    sources = Sources()
    sources.appendFromDirectory(path)
    compiler.appendSource(sources.sources)
    compiler.appendAllIncludePath(path)
    return compiler

def printInformation():
    print ''
    print '-------------------------'
    print 'WORLD SCons build script.'
    print '-------------------------'
    print Config().getInformation()
    print ''

## ----------------
## Runner function.
## ----------------

def getWorldCompiler():
    name = 'world'
    libs = library.getMainStaticLibs(Config().isMacOSX())
    compiler = getDefaultCompiler(name, libs)

    #compiler.appendAllCflag('-Wextra') # Don't use Wextra flag.
    compiler.appendAllCflag('-Wall')

    # Third-party
    addThirdPartySetting(compiler, 'src/3rd/sqlite')
    addThirdPartySetting(compiler, 'src/3rd/lua')
    addThirdPartySetting(compiler, 'src/3rd/lmdb')
    compiler.appendAllDefine('LUA_COMPAT_5_2')

    if not Config().debug:
        compiler.appendAllCflag('-O2')

    return compiler

def getWorlditCompiler():
    name = 'worldit'
    libs = library.getMainStaticLibs(Config().isMacOSX()) + library.getSdl2StaticLibs()
    compiler = getDefaultCompiler(name, libs)

    #compiler.appendAllCflag('-Wextra') # Don't use Wextra flag.
    compiler.appendAllCflag('-Wall')

    # Third-party
    addThirdPartySetting(compiler, 'src/3rd/gfx')

    if Config().windows:
        compiler.appendLinkflag(['-Wl,--subsystem,windows', '-mwindows'])
    if not Config().debug:
        compiler.appendAllCflag('-O2')

    return compiler

def getTestCompiler():
    name = 'test'
    libs = library.getMainStaticLibs(Config().isMacOSX()) + library.getTestStaticLibs()
    return getDefaultCompiler(name, libs)


def build():
    compilerWorld   = getWorldCompiler()
    compilerWorldit = getWorlditCompiler()
    compilerTest    = getTestCompiler()

    compilerWorldit.prependLibrary(compilerWorld.name)
    compilerTest.prependLibrary(compilerWorld.name)

    world   = compilerWorld.runLibrary()
    worldit = compilerWorldit.runProgram()
    test    = compilerTest.runProgram()

    Depends(worldit, world)
    Depends(test,    world)
    pass


## ------
## RUN!!!
## ------

printInformation()
build()

library.py file

#!/bin/python
# -*- coding: utf-8 -*-

def addSuffix(array, suffix):
    result = []
    for cursor in array:
        result += [ cursor + suffix ]
    return result

def addPrefix(array, prefix):
    result = []
    for cursor in array:
        result += [ prefix + cursor ]
    return result


## ----------
## Libraries.
## ----------

BOOST_LIBS = ['boost_system', 'boost_filesystem', 'boost_locale', 'boost_log', 'boost_log_setup'
              , 'boost_thread' # for the boost_log (use from basic_formatting_sink_frontend)
              ]
TEST_LIBS  = ['boost_unit_test_framework']
SDL2_LIBS  = ['SDL2', 'SDL2_image', 'SDL2_mixer', 'SDL2_ttf']

MULTI_THREADING = '-mt'
DYNAMIC_SUFFIX = '.dll'

def getBoostStaticLibs(boost_mt = False):
    if boost_mt:
        return addSuffix(BOOST_LIBS, MULTI_THREADING)
    else:
        return BOOST_LIBS

def getTestStaticLibs():
    return TEST_LIBS
def getSdl2StaticLibs():
    return SDL2_LIBS

def getBoostDynamicLibs(boost_mt = False):
    return addSuffix(BOOST_LIBS, DYNAMIC_SUFFIX)
def getTestDynamicLibs():
    return addSuffix(TEST_LIBS, DYNAMIC_SUFFIX)
def getSdl2DynamicLibs():
    return addSuffix(SDL2_LIBS, DYNAMIC_SUFFIX)


## -----------------
## User's interface.
## -----------------

def getMainStaticLibs(boost_mt = False):
    return getBoostStaticLibs(boost_mt)


if __name__ == '__main__':
    pass

Worldit SConstruct v2

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys
import platform
import time
import fnmatch
import re

DOT  = '.'
DASH = '-'

DEBUG_CHAR = 'g'
DEBUG_FLAG = DASH + DEBUG_CHAR

SOURCE_SUFFIX = DOT + 'cpp'
OBJECT_SUFFIX = DOT + 'o'

DEBUG_DEFINE   = 'DEBUG'
NDEBUG_DEFINE  = 'NDEBUG'
RELEASE_DEFINE = 'RELEASE'

## ------------
## Time & Date.
## ------------

TIME_FORMAT = r'%y%m%d_%H%M%S'

def getModification(path):
    return time.localtime(os.path.getmtime(path))

def getTimestamp(time):
    return time.strftime(TIME_FORMAT, time)

## ---------
## Platform.
## ---------

PLATFORM_MACOSX  = 'darwin'
PLATFORM_LINUX   = 'linux2'
PLATFORM_WINDOWS = 'win32'
PLATFORM_CYGWIN  = 'cygwin'
PLATFORM_OS2     = 'os2'
PLATFORM_OS2_EMX = 'os2emx'
PLATFORM_RISC_OS = 'riscos'
PLATFORM_ATHE_OS = 'atheos'
PLATFORM_UNKNOWN = 'unknown'

def getPlatform():
    return sys.platform

def isPosix():
    return os.name.upper() == 'POSIX'

def getMachine():
    return platform.machine()

## -----------------
## File & Directory.
## -----------------

CPP_SOURCE_SUFFIX_LIST = ['*.c', '*.cc', '*.cxx', '*.cpp', '*.c++']
CPP_HEADER_SUFFIX_LIST = ['*.h', '*.hh', '*.hxx', '*.hpp', '*.h++']
CPP_SUFFIX_LIST = CPP_SOURCE_SUFFIX_LIST + CPP_HEADER_SUFFIX_LIST
PROTO_SUFFIX_LIST = ['*.proto']

def findWithSuffix(directory, suffix):
    result = []
    for dirpath, dirnames, filenames in os.walk(directory):
        for ext in suffix:
            for filename in fnmatch.filter(filenames, ext):
                result += [os.path.join(dirpath, filename)]
    return result

def existAndMkdir(dir):
    if not os.path.isdir(dir):
        os.mkdir(dir)

## -----------
## Properties.
## -----------

PROPERTIES_NAME      = 'properties'
CPATH_NAME           = 'CPATH'
LIBRARY_PATH_NAME    = 'LIBRARY_PATH'
LD_LIBRARY_PATH_NAME = 'LD_LIBRARY_PATH_NAME'

if not os.path.isfile(PROPERTIES_NAME):
    with open(PROPERTIES_NAME, 'wb') as f:
        f.write(CPATH_NAME           + '=\n')
        f.write(LIBRARY_PATH_NAME    + '=\n')
        f.write(LD_LIBRARY_PATH_NAME + '=\n')

def readProperties():
    with open(PROPERTIES_NAME, 'rb') as f:
        result = {}
        for line in f:
            name  = str(re.match(r'^.*=', line).group()).strip()
            value = str(line[len(name):]).strip()
            name  = str(name[:-1]).strip()
            result[name] = value
    return result

## ----------------
## SCons arguments.
## ----------------

DEBUG_PARAM_NAME = 'debug'

def getArgument(name, default):
    return int(ARGUMENTS.get(name, default)) # NOQA

IS_DEBUG = getArgument(DEBUG_PARAM_NAME, 0)

## ----------
## FILE NAME.
## ----------

def getChangeSuffix(original, suffix):
    dot_index = original.rfind(DOT)
    if dot_index == -1:
        return original + suffix
    return original[:dot_index] + suffix

def getFlagSuffix(debug=False):
    if debug:
        return DEBUG_FLAG
    return ''

def getDecorateName(original, suffix='', debug=False):
    flags = getFlagSuffix(debug)
    return getChangeSuffix(original, flags + suffix)

def getAutoDecorateName(original, suffix=''):
    debug = IS_DEBUG
    return getDecorateName(original, suffix, debug)

def getObjectName(original):
    return getAutoDecorateName(original, OBJECT_SUFFIX)

def getObject(env, source, **dic):
    obj_name = getObjectName(source)
    return env.Object(obj_name, source, **dic)

## ---------------
## COMMON SETTING.
## ---------------

def setProperties(env):
    properties = readProperties()
    for key in properties.keys():
        if properties[key]:
            env.AppendENVPath(key, properties[key])

def setReleaseMode(env):
    env.Append(CCFLAGS=['-O2'])
    env.Append(CPPDEFINES=[NDEBUG_DEFINE, RELEASE_DEFINE])

def setDebugMode(env):
    env.Append(CCFLAGS=[DEBUG_FLAG])
    env.Append(CPPDEFINES=[DEBUG_DEFINE])

def setDefaultSetting(env):
    if IS_DEBUG:
        setDebugMode(env)
    else:
        setReleaseMode(env)
    env.Append(CPPPATH=['src', '3rd'])
    env.Append(CXXFLAGS=['-std=c++11'])
    # env.Append(CCFLAGS=['-fPIC'])
    # env.Append(LINKFLAGS=['-static-libstdc++'])
    # env.Append(LINKFLAGS=['-stdlib=libstdc++'])
    # env.Append(LIBS=['stdc++'])

## ---------
## Protobuf.
## ---------

def getProtobufObject(env, in_dir, out_dir, source, **dic):
    command = 'protoc -I ' + in_dir + ' --cpp_out=' + out_dir + ' $SOURCE'
    source_name = os.path.splitext(os.path.basename(source))[0]
    cpp_header  = os.path.join(out_dir, source_name + '.pb.h')
    cpp_source  = os.path.join(out_dir, source_name + '.pb.cc')
    env.Command([cpp_header, cpp_source], source, [command])
    return getObject(env, cpp_source, **dic)

def getProtobufObjectList(env, **dic):
    out_dir = 'src/proto'
    in_dir  = 'src/proto'
    source  = findWithSuffix(in_dir, PROTO_SUFFIX_LIST)
    existAndMkdir(out_dir)

    result = []
    for cursor in source:
        result += getProtobufObject(env, in_dir, out_dir, cursor, **dic)
    return result

## --------
## BUILDER.
## --------

def createEnvironment(**dic):
    env = Environment(ENV=os.environ, **dic) # NOQA
    setProperties(env)
    return env

def createFileList(env, target, source):
    result = str(target[0])
    if os.path.isfile(result):
        os.remove(result)
    with open(result, 'wb') as f:
        index = 0
        while index < len(source):
            f.write(str(source[index]) + '\n')
            index += 1

## ----------
## Libraries.
## ----------

LIB_PROTO_PREFIX = 'proto'
LIB_THIRD_PREFIX = 'third'
LIB_MAIN_PREFIX = 'world'

def setupProtoBuilder():
    env  = createEnvironment()
    name = LIB_PROTO_PREFIX
    setDefaultSetting(env)
    objs = getProtobufObjectList(env)
    return env.StaticLibrary(getAutoDecorateName(name), source=objs)

def setupThirdBuilder():
    env  = createEnvironment()
    name = LIB_THIRD_PREFIX
    setDefaultSetting(env)
    objs = []
    for cursor in findWithSuffix('3rd', CPP_SOURCE_SUFFIX_LIST):
        objs += getObject(env, cursor)
    env.Append(CCFLAGS=['-w'])
    env.Append(CPPDEFINES=['LUA_COMPAT_5_2'])
    return env.StaticLibrary(getAutoDecorateName(name), source=objs)

def setupMainBuilder():
    env  = createEnvironment()
    name = LIB_MAIN_PREFIX
    setDefaultSetting(env)
    objs = []
    for cursor in findWithSuffix('src/world', CPP_SOURCE_SUFFIX_LIST):
        objs += getObject(env, cursor)
    env.Append(CCFLAGS=['-Wall'])
    return env.StaticLibrary(getAutoDecorateName(name), source=objs)

proto = setupProtoBuilder()
third = setupThirdBuilder()
main  = setupMainBuilder()

def setDefaultLibraries(env):
    env.Prepend(LIBS=[getAutoDecorateName(LIB_PROTO_PREFIX),
                      getAutoDecorateName(LIB_THIRD_PREFIX),
                      getAutoDecorateName(LIB_MAIN_PREFIX)])
    if getPlatform() == PLATFORM_LINUX:
        env.Append(LIBS=['pthread'])

## --------
## Program.
## --------

TEST_PREFIX   = 'test'
SAMPLE_PREFIX = 'sample'

def setupTestBuilder():
    env  = createEnvironment()
    name = TEST_PREFIX
    setDefaultSetting(env)
    setDefaultLibraries(env)
    env.Prepend(LIBS=['gtest', 'gtest_main'])
    objs = []
    for cursor in findWithSuffix('src/test', CPP_SOURCE_SUFFIX_LIST):
        objs += getObject(env, cursor)
    return env.Program(getAutoDecorateName(name), source=objs)

def setupSampleBuilder():
    env  = createEnvironment()
    name = SAMPLE_PREFIX
    setDefaultSetting(env)
    setDefaultLibraries(env)
    objs = []
    objs += getObject(env, 'src/sample/main.cpp')
    return env.Program(getAutoDecorateName(name), source=objs)

test   = setupTestBuilder()
sample = setupSampleBuilder()

## ----------------
## Target settings.
## ----------------

def setDepends(target, depends):
    Depends(target, depends) # NOQA

def setDefault(targets):
    Default(targets) # NOQA

LIB_TARGETS = [proto, third, main]

setDepends(test,   LIB_TARGETS)
setDepends(sample, LIB_TARGETS)

DEFAULT_TARGETS  = LIB_TARGETS
DEFAULT_TARGETS += [test, sample]

setDefault(DEFAULT_TARGETS) # NOQA

if __name__ == '__main__':
    pass

See also