import os

build_path = 'build'

supported_architectures = ['x64', 'x86', 'arm', 'aarch64', 'mips', 'mipsel', 'mips64', 'mips64el', 'ppc', 'ppc64', 'ppc64le']
skip_for_pe = ['cwe_782.c', 'cwe_426.c', 'cwe_243.c', 'cwe_243_clean.c']

c_compilers = {'x64': ['gcc', 'x86_64-w64-mingw32-gcc', 'clang'],
               'x86': ['gcc', 'i686-w64-mingw32-gcc', 'clang'],
               'arm': ['arm-linux-gnueabi-gcc', 'clang'],
               'aarch64': ['aarch64-linux-gnu-gcc', 'clang'],
               'mips': ['mips-linux-gnu-gcc', 'clang'],
               'mipsel': ['mipsel-linux-gnu-gcc', 'clang'],
               'mips64': ['mips64-linux-gnuabi64-gcc', 'clang'],
               'mips64el': ['mips64el-linux-gnuabi64-gcc', 'clang'],
               'ppc': ['powerpc-linux-gnu-gcc'],
               'ppc64': ['powerpc64-linux-gnu-gcc', 'clang'],
               'ppc64le': ['powerpc64le-linux-gnu-gcc', 'clang']}

cpp_compilers = {'x64': ['g++', 'x86_64-w64-mingw32-g++', 'clang++'],
                 'x86': ['g++', 'i686-w64-mingw32-g++', 'clang++'],
                 'arm': ['arm-linux-gnueabi-g++'],
                 'aarch64': ['aarch64-linux-gnu-g++'],
                 'mips': ['mips-linux-gnu-g++'],
                 'mipsel': ['mipsel-linux-gnu-g++'],
                 'mips64': ['mips64-linux-gnuabi64-g++'],
                 'mips64el': ['mips64el-linux-gnuabi64-g++'],
                 'ppc': ['powerpc-linux-gnu-g++'],
                 'ppc64': ['powerpc64-linux-gnu-g++'],
                 'ppc64le': ['powerpc64le-linux-gnu-g++']}

flags = {'x64': ' -g -fno-stack-protector -std=c11',
         'x86': ' -g -m32 -fno-stack-protector -std=c11',
         'arm': ' -g -fno-stack-protector -std=c11',
         'aarch64': ' -g -fno-stack-protector -std=c11',
         'mips': ' -g -fno-stack-protector -std=c11',
         'mipsel': ' -g -fno-stack-protector -std=c11',
         'mips64': ' -g -fno-stack-protector -std=c11',
         'mips64el': ' -g -fno-stack-protector -std=c11',
         'ppc': ' -g -fno-stack-protector -std=c11',
         'ppc64': ' -g -fno-stack-protector -std=c11',
         'ppc64le': ' -g -fno-stack-protector -std=c11'}

target_flags = {'x64': '',
               'x86': ' -m32 ',
               'arm': ' -target arm-linux-gnueabi',
               'aarch64': ' -target aarch64-linux-gnu',
               'mips': ' -target mips-linux-gnu',
               'mipsel': ' -target mipsel-linux-gnu',
               'mips64': ' -target mips64-linux-gnuabi64',
               'mips64el': ' -target mips64el-linux-gnuabi64',
               'ppc64': ' -target powerpc64-linux-gnu',
               'ppc64le': ' -target powerpc64le-linux-gnu'}


cpp_flags = {'x64': ' -g -fno-stack-protector',
             'x86': ' -m32 -g -fno-stack-protector',
             'arm': ' -g -fno-stack-protector',
             'aarch64': ' -g -fno-stack-protector',
             'mips': ' -g -fno-stack-protector',
             'mipsel': ' -g -fno-stack-protector',
             'mips64': ' -g -fno-stack-protector',
             'mips64el': ' -g -fno-stack-protector',
             'ppc': ' -g -fno-stack-protector',
             'ppc64': ' -g -fno-stack-protector',
             'ppc64le': ' -g -fno-stack-protector',}

def which(pgm):
    # check compilers on path
    path = os.getenv('PATH')
    for p in path.split(os.path.pathsep):
        p = os.path.join(p,pgm)
        if os.path.exists(p) and os.access(p,os.X_OK):
            return p
    print('Compiler %s not in path!' % pgm)

    return None


def optimize(filename):
    optimize_me = ["cwe_119.c"]
    if filename in optimize_me:
        return ' -O1'
    else:
        return ' -O0'


def get_compiler_abrev(compiler_name):
    if 'clang' == compiler_name:
        return 'clang'
    if 'clang++' == compiler_name:
        return 'clang++'
    if 'mingw32-g++' in compiler_name:
        return 'mingw32-g++'
    if 'g++' in compiler_name:
        return 'g++'
    if 'mingw32-gcc' in compiler_name:
        return 'mingw32-gcc'

    return 'gcc'


def compile_only_on_x64(filename, arch):
    only_x64 = ['cwe_782.c']
    return filename in only_x64 and arch != 'x64'


def build_c(arch, compiler):
    if which(compiler) is not None:
        for prog in Glob('*.c'):
            if compile_only_on_x64(str(prog), arch):
                print('Skipping architecture %s for %s' % (arch, str(prog)))
                continue

            env = Environment()
            env['CC'] = compiler
            if compiler == 'clang':
                env['CCFLAGS'] = target_flags[arch] + flags[arch] + optimize(str(prog))
                env['LINKFLAGS'] = target_flags[arch]
            else:
                env['CCFLAGS'] = flags[arch] + optimize(str(prog))
                if arch == 'x86':
                    env['LINKFLAGS'] = '-m32'

            compiler_abrev = get_compiler_abrev(compiler)
            if compiler_abrev == 'mingw32-gcc' and str(prog) in skip_for_pe:
                continue
            env.Program('%s/%s_%s_%s.out' % (build_path, str(prog).split('.')[0], arch, compiler_abrev),
                        env.Object(target='%s/%s_%s_%s.o' % (build_path, str(prog), arch, compiler_abrev),
                        source='%s/%s' % (build_path, str(prog))))
    else:
        print('Compiler %s for architecture %s is not installed!' % (compiler, arch))


def build_cpp(arch, compiler):
    if which(compiler) is not None:
        for prog in Glob('*.cpp'):
            env = Environment()
            env['CXX'] = compiler
            env['CXXFLAGS'] = cpp_flags[arch] + optimize(str(prog))
            if arch == 'x86':
                env['LINKFLAGS'] = '-m32'
            compiler_abrev = get_compiler_abrev(compiler)
            if compiler_abrev == 'mingw32-g++' and str(prog) in skip_for_pe:
                    continue
            env.Program('%s/%s_%s_%s.out' % (build_path, str(prog).split('.')[0], arch, compiler_abrev),
                        env.Object(target='%s/%s_%s_%s.o' % (build_path, str(prog), arch, compiler_abrev),
                        source='%s/%s' % (build_path, str(prog))))
    else:
        print('Compiler %s for architecture %s is not installed!' % (compiler, arch))


VariantDir(build_path, '.', duplicate=0)
for arch in supported_architectures:
    print('Building for architecture %s' % arch)
    for compiler in c_compilers[arch]:
        build_c(arch, compiler)
    for compiler in cpp_compilers[arch]:
        build_cpp(arch, compiler)
