diff --git a/SConstruct b/SConstruct index d9f62e9b..92ce34fb 100644 --- a/SConstruct +++ b/SConstruct @@ -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) + diff --git a/src/SConscript b/src/SConscript index 88b53216..69be8700 100644 --- a/src/SConscript +++ b/src/SConscript @@ -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)