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. """ SConstruct file fore building Kvazaar with Scons.
This file defines two targets, x86 and x64, and builds construction This is an additional cross platform way to build the program.
environments for both. The main way is to use the Makefile and that is always kept up to date.
TODO: This mostly exists so it can be used with a custom SConscript file that
- add debug builds builds any old version of Kvazaar, ensuring that the compilation settings are
- whole program optimization for gcc 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 os
import platform import platform
Help("""
Type: 'scons x86' to build a 32-bit release version, vars = Variables()
'scons x64' to build a 64-bit release version.
'scons x86 x64 -c' to clear all build directories. 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 # Visual studio needs the architecture to be set in this stage. It can not be
# modified later. # modified later. Other tools ignore TARGET_ARCH.
env_x86 = Environment( # The variable substitution is done in the environment construction so $arch
#tools=['mingw'], # will not work here. Get the value manually.
ASCOM='yasm $ASFLAGS -o $TARGET $SOURCES', arch = ARGUMENTS.get('arch', default_arch)
ENV={'PATH': os.environ['PATH']}, # to find yasm on Windows if arch == 'x64':
TARGET_ARCH='x86', # for Visual Studio arch = 'amd64'
) env = Environment(
env_x64 = Environment( variables=vars,
#tools=['mingw'], tools=['msvc', 'mslink', 'nasm'],
ASCOM='yasm $ASFLAGS -o $TARGET $SOURCES', ENV={'PATH': os.environ['PATH']}, # to find yasm
ENV={'PATH': os.environ['PATH']}, # to find yasm on Windows TARGET_ARCH=arch, # for Visual Studio
TARGET_ARCH='amd64', # 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 # Try and deal with all the remaining compiler specific differences that I
# really with scons would handle. # really wish scons would handle.
if 'MSVS' in env_x86: if compiler in ('msvs',):
# /MD = multithreaded DLL runtime arch_dir = {'x86': r'x86', 'amd64': r'x64'}[env['arch']]
# /Ox = full optimization, sets /Ob2, /Og, /Oi, /Ot, /Oy pthreads_dll = os.path.join(env['win32pthreads'], 'dll', arch_dir, 'pthreadVC2.dll')
# /GL = enable whole program optimization
# /LTCG = link time code generation env.Append(
# /arch:SSE2 = use SSE2 (x86 only) CCFLAGS=r'/MD /Ox /GL /wd"4028"',
# win32-pthreads is assumed to be in same level as kvazaar in dir pthreads LINKFLAGS=r'/LTCG',
env_x86.Append( LIBPATH=r'#$win32pthreads\lib\\' + arch_dir,
CCFLAGS=r'/MD /Ox /GL /arch:SSE2 /I"..\..\pthreads\include"', LIBS=r'pthreadVC2',
LINKFLAGS=r'/LTCG /LIBPATH:"..\..\pthreads\x86" "pthreadVC2.lib"') CPPPATH=r'#$win32pthreads\include')
env_x64.Append(
CCFLAGS=r'/MD /Ox /GL /I"..\..\pthreads\include"',
LINKFLAGS=r'/LTCG /LIBPATH:"..\pthreads\x64" "pthreadVC2.lib"')
else: else:
# GCC flags env.MergeFlags('-O2 -pthread -lm -lrt -march=native')
# -m for arch, -O2 for optimization, -lm for math lib if env['arch'] == 'x86':
env_x86.MergeFlags('-m32 -O2 -lm -march=native -pthread') env.MergeFlags('-m32')
env_x64.MergeFlags('-m64 -O2 -lm -march=native -pthread') 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. # VS2010 linker and mingw64 need TMP.
if 'TMP' in os.environ: if compiler in ('msvs', 'mingw'):
env_x86['ENV']['TMP'] = os.environ['TMP'] if 'TMP' in os.environ:
env_x64['ENV']['TMP'] = os.environ['TMP'] env['ENV']['TMP'] = os.environ['TMP']
env_x86.MergeFlags('-I. -Iextras -Istrategies') env.MergeFlags('-I. -Iextras -Istrategies')
env_x64.MergeFlags('-I. -Iextras -Istrategies')
# Take comma separated list from 'D=' and pass them on to
# preprocessor as defines.
preprocessor_defines = ARGUMENTS.get('D', '') preprocessor_defines = ARGUMENTS.get('D', '')
if preprocessor_defines: if preprocessor_defines:
for define in preprocessor_defines.split(): for define in preprocessor_defines.split(','):
env_x86.MergeFlags('-D' + define) env.MergeFlags('-D' + define)
env_x64.MergeFlags('-D' + define)
# Declare build targets. # Declare build targets.
x86 = SConscript('src/SConscript', variant_dir = 'scons_build'
exports={'env': env_x86},
variant_dir='scons_build_x86',
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) 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'): # Copy exe and dll's to the path from 'copyto=' argument.
Default(x64) 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: 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 os
Import('env') Import('envs')
env = envs.env
sources = [] sources = []
sources += env.Glob('*.c') sources += env.Glob('*.c')
sources += env.Glob('extras/*.c') sources += env.Glob('extras/*.c')
sources += env.Glob('strategies/*.c') sources += env.Glob('strategies/*.c')
sources += env.Glob('x86/*.asm') sources += env.Glob('strategies/generic/*.c')
env.Depends('x86/cpu.asm', 'x86/x86inc.asm') 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) prog = env.Program('kvazaar', sources)