Rewrite the SContruct.

- Works with new /strategy/ structure.
- Change architecture selection to use arch= instead of construction target.
This commit is contained in:
Ari Koivula 2014-08-19 18:12:04 +03:00
parent 4622d8392c
commit 4c3bbd4a35
2 changed files with 220 additions and 89 deletions

View file

@ -1,116 +1,236 @@
""" SConstruct file fore building Kvazaar with Scons.
This file defines two targets, x86 and x64, and builds construction
environments for both.
This is an additional cross platform way to build the program.
The main way is to use the Makefile and that is always kept up to date.
TODO:
- add debug builds
- whole program optimization for gcc
This mostly exists so it can be used with a custom SConscript file that
builds any old version of Kvazaar, ensuring that the compilation settings are
the same and any compilation from a specific version of Kvazaar will not
affect the comparison. The SConscript included in git version only compiles
the current version.
"""
import os
import platform
Help("""
Type: 'scons x86' to build a 32-bit release version,
'scons x64' to build a 64-bit release version.
'scons x86 x64 -c' to clear all build directories.
""")
vars = Variables()
default_arch = 'amd64' if platform.machine().endswith('64') else 'x86'
vars.Add(EnumVariable('arch', 'Set target arch.', default_arch,
allowed_values=('x86', 'x64', 'amd64', 'ppc'),
map={'x64': 'amd64'}))
vars.Add(PathVariable('win32pthreads',
'Path to win32-pthreads dir.',
r'./../pthreads.2'))
vars.Add(PathVariable('copyto',
'Copy exe and required DLLs to this dir.',
'',
PathVariable.PathAccept))
vars.Add(BoolVariable('dump_env',
'Dump of construction environment to stdout.',
False))
vars.Add(BoolVariable('use_yasm',
'Use yasm.',
True))
# Create construction environments for 32 and 64 bit builds.
# Visual studio needs the architecture to be set in this stage. It can not be
# modified later.
env_x86 = Environment(
#tools=['mingw'],
ASCOM='yasm $ASFLAGS -o $TARGET $SOURCES',
ENV={'PATH': os.environ['PATH']}, # to find yasm on Windows
TARGET_ARCH='x86', # for Visual Studio
)
env_x64 = Environment(
#tools=['mingw'],
ASCOM='yasm $ASFLAGS -o $TARGET $SOURCES',
ENV={'PATH': os.environ['PATH']}, # to find yasm on Windows
TARGET_ARCH='amd64', # for Visual Studio
# modified later. Other tools ignore TARGET_ARCH.
# The variable substitution is done in the environment construction so $arch
# will not work here. Get the value manually.
arch = ARGUMENTS.get('arch', default_arch)
if arch == 'x64':
arch = 'amd64'
env = Environment(
variables=vars,
tools=['msvc', 'mslink', 'nasm'],
ENV={'PATH': os.environ['PATH']}, # to find yasm
TARGET_ARCH=arch, # for Visual Studio
AS='yasm',
)
# YASM flags for different architectures. The object file format and name
# mangling must be the same as used by the C compiler for that OS.
# Indexed by platform.system().
yasm_flags = {
'Windows': {
'x86': '-f win32 -DPREFIX',
'x64': '-f win64'},
'Darwin': {
'x86': '-f macho32 -DPREFIX',
'x64': '-f macho64 -DPREFIX'},
'Linux': { # Flags for Unix-like.
'x86': '-f elf32',
'x64': '-f elf64'},
'all': { # Flags for all systems.
'x86': ' -DARCH_X86_64=0 -m x86',
'x64': ' -DARCH_X86_64=1 -m amd64'},
}
# Set yasm flags for OS and architecture.
target_yasm_flags = yasm_flags.get(platform.system(), yasm_flags['Linux'])
env_x86.Replace(ASFLAGS=target_yasm_flags['x86'])
env_x64.Replace(ASFLAGS=target_yasm_flags['x64'])
env_x86.Append(ASFLAGS=yasm_flags['all']['x86'])
env_x64.Append(ASFLAGS=yasm_flags['all']['x64'])
Help("""
Example: 'scons arch=x64' to compile for amd64.
'scons --jobs=8' to compile in parallel.
""" + vars.GenerateHelpText(env))
if 'MSVS' in env:
compiler = 'msvs'
elif 'MSYSTEM' in os.environ:
compiler = 'mingw'
else:
compiler = 'gcc'
env['use_yasm'] = env['use_yasm'] and env['arch'] not in ('ppc',)
# pthreads_dll location for copyto
pthreads_dll = None
# Try and deal with all the remaining compiler specific differences that I
# really with scons would handle.
if 'MSVS' in env_x86:
# /MD = multithreaded DLL runtime
# /Ox = full optimization, sets /Ob2, /Og, /Oi, /Ot, /Oy
# /GL = enable whole program optimization
# /LTCG = link time code generation
# /arch:SSE2 = use SSE2 (x86 only)
# win32-pthreads is assumed to be in same level as kvazaar in dir pthreads
env_x86.Append(
CCFLAGS=r'/MD /Ox /GL /arch:SSE2 /I"..\..\pthreads\include"',
LINKFLAGS=r'/LTCG /LIBPATH:"..\..\pthreads\x86" "pthreadVC2.lib"')
env_x64.Append(
CCFLAGS=r'/MD /Ox /GL /I"..\..\pthreads\include"',
LINKFLAGS=r'/LTCG /LIBPATH:"..\pthreads\x64" "pthreadVC2.lib"')
# really wish scons would handle.
if compiler in ('msvs',):
arch_dir = {'x86': r'x86', 'amd64': r'x64'}[env['arch']]
pthreads_dll = os.path.join(env['win32pthreads'], 'dll', arch_dir, 'pthreadVC2.dll')
env.Append(
CCFLAGS=r'/MD /Ox /GL /wd"4028"',
LINKFLAGS=r'/LTCG',
LIBPATH=r'#$win32pthreads\lib\\' + arch_dir,
LIBS=r'pthreadVC2',
CPPPATH=r'#$win32pthreads\include')
else:
# GCC flags
# -m for arch, -O2 for optimization, -lm for math lib
env_x86.MergeFlags('-m32 -O2 -lm -march=native -pthread')
env_x64.MergeFlags('-m64 -O2 -lm -march=native -pthread')
env.MergeFlags('-O2 -pthread -lm -lrt -march=native')
if env['arch'] == 'x86':
env.MergeFlags('-m32')
elif env['arch'] == 'amd64':
env.MergeFlags('-m64')
# platform.system() lies on msys2, so just try to detect msys/mingw.
if 'MSYSTEM' in os.environ:
# __USE_MINGW_ANSI_STDIO required on mingw for printf to function.
env.MergeFlags('-D' + '__USE_MINGW_ANSI_STDIO=1')
# VS2010 linker and mingw64 need TMP.
if 'TMP' in os.environ:
env_x86['ENV']['TMP'] = os.environ['TMP']
env_x64['ENV']['TMP'] = os.environ['TMP']
if compiler in ('msvs', 'mingw'):
if 'TMP' in os.environ:
env['ENV']['TMP'] = os.environ['TMP']
env_x86.MergeFlags('-I. -Iextras -Istrategies')
env_x64.MergeFlags('-I. -Iextras -Istrategies')
env.MergeFlags('-I. -Iextras -Istrategies')
# Take comma separated list from 'D=' and pass them on to
# preprocessor as defines.
preprocessor_defines = ARGUMENTS.get('D', '')
if preprocessor_defines:
for define in preprocessor_defines.split():
env_x86.MergeFlags('-D' + define)
env_x64.MergeFlags('-D' + define)
for define in preprocessor_defines.split(','):
env.MergeFlags('-D' + define)
# Declare build targets.
x86 = SConscript('src/SConscript',
exports={'env': env_x86},
variant_dir='scons_build_x86',
variant_dir = 'scons_build'
class EnvinronmentContainer(object):
"""Class for initializing and holding optimization environments.
As far as I know, making a separate environment is the only way to compile
different objects with different flags in scons. So this constructs all
the required environments.
Yasm is also its own environment although it's not strictly necessary.
It does however allow KVZ_COMPILE_ASM to only those files that require
it, avoiding a complete recompile.
"""
def __init__(self, env, compiler, arch):
# If optimization is not supported for arch, use default env.
self.env = env
self.sse2 = env
self.sse41 = env
self.avx = env
self.avx2 = env
self.altivec = env
self.asm = env
if compiler == 'msvs':
self.avx = env.Clone()
self.avx.Append(CCFLAGS='/arch:AVX')
self.avx2 = env.Clone()
self.avx2.Append(CCFLAGS='/arch:AVX2')
elif compiler in ('gcc',) and arch in ('x86', 'x64'):
self.sse2 = env.Clone()
self.sse2.Append(CCFLAGS='-msse2')
self.sse41 = env.Clone().Append(CCFLAGS='-msse4.1')
self.avx = env.Clone().Append(CCFLAGS='-mavx')
self.avx2 = env.Clone().Append(CCFLAGS='-mavx2')
elif compiler in ('gcc',) and arch in ('ppc'):
self.altivec = env.Clone().Append(CCFLAGS='-maltivec')
if env['use_yasm']:
self._init_yasm()
def _init_yasm(self):
self.asm = env.Clone()
self.asm.MergeFlags('-D' + 'KVZ_COMPILE_ASM')
# YASM flags for different architectures. The object file format and name
# mangling must be the same as used by the C compiler for that OS.
# Indexed by platform.system().
yasm_flags = {
'Windows': {
'x86': '-f win32 -DPREFIX -DHAVE_ALIGNED_STACK=0 ',
'amd64': '-f win64 -DHAVE_ALIGNED_STACK=1',
},
'Darwin': {
'x86': '-f macho32 -DPREFIX',
'amd64': '-f macho64 -DPREFIX',
},
'Linux': { # Flags for Unix-like.
'x86': '-f elf32',
'amd64': '-f elf64',
},
'all': { # Flags for all systems.
'x86': ' -I./src/extras -DARCH_X86_64=0 -m x86',
'amd64': ' -I./src/extras -DARCH_X86_64=1 -m amd64',
},
}
# Set yasm flags for OS and architecture.
target_yasm_flags = yasm_flags.get(platform.system(), yasm_flags['Linux'])
self.asm.Replace(ASFLAGS=target_yasm_flags[env['arch']])
self.asm.Append(ASFLAGS=yasm_flags['all'][env['arch']])
self.asm.Replace(AS='yasm')
envs = EnvinronmentContainer(env, compiler, env['arch'])
program = SConscript('src/SConscript',
exports={'envs': envs},
variant_dir=variant_dir,
duplicate=False)
Alias('x86', x86)
x64 = SConscript('src/SConscript',
exports={'env': env_x64},
variant_dir='scons_build_x64',
duplicate=False)
Alias('x64', x64)
if platform.machine().endswith('64'):
Default(x64)
# Copy exe and dll's to the path from 'copyto=' argument.
copy_dir = env['copyto']
if copy_dir:
env.Install(copy_dir, program)
if pthreads_dll:
env.Install(copy_dir, pthreads_dll)
env.Alias('copyto', copy_dir)
Default([program, 'copyto'])
else:
Default(x86)
Default(program)
# Dumpo environment to stdout.
if env['dump_env']:
import difflib
def get_diff(orig_env, new_env):
return "\n".join(difflib.unified_diff(orig_env, new_env.Dump().splitlines(), n=0))
env_dump = env.Dump().splitlines()
print "== env"
print "\n".join(env_dump)
print "== diff env envs.sse2"
print get_diff(env_dump, envs.sse2)
print "== diff env envs.sse41"
print get_diff(env_dump, envs.sse41)
print "== diff env envs.avx"
print get_diff(env_dump, envs.avx)
print "== diff env envs.avx2"
print get_diff(env_dump, envs.avx2)
print "== diff env envs.altivec"
print get_diff(env_dump, envs.altivec)
print "== diff env envs.asm"
print get_diff(env_dump, envs.asm)

View file

@ -3,14 +3,25 @@
import os
Import('env')
Import('envs')
env = envs.env
sources = []
sources += env.Glob('*.c')
sources += env.Glob('extras/*.c')
sources += env.Glob('strategies/*.c')
sources += env.Glob('x86/*.asm')
env.Depends('x86/cpu.asm', 'x86/x86inc.asm')
sources += env.Glob('strategies/generic/*.c')
sources += envs.sse2.Object(env.Glob('strategies/sse2/*.c'))
sources += envs.sse41.Object(env.Glob('strategies/sse41/*.c'))
sources += envs.avx.Object(env.Glob('strategies/avx/*.c'))
sources += envs.avx2.Object(env.Glob('strategies/avx2/*.c'))
sources += envs.altivec.Object(env.Glob('strategies/altivec/*.c'))
sources += envs.asm.Object(env.Glob('strategies/x86_asm/*.c'))
if env['use_yasm']:
sources += envs.asm.Object(env.Glob('strategies/x86_asm/*.asm'))
sources += envs.asm.Object(env.Glob('extras/*.asm'))
prog = env.Program('kvazaar', sources)