#!/usr/bin/env python """Ninja toolchain abstraction for GCC compiler suite""" import os import toolchain class GCCToolchain(toolchain.Toolchain): def initialize(self, project, archs, configs, includepaths, dependlibs, libpaths, variables, subninja): #Local variable defaults self.toolchain = '' self.includepaths = [] self.libpaths = libpaths self.ccompiler = os.environ.get('CC') or 'gcc' self.cxxcompiler = os.environ.get('CXX') or 'g++' self.archiver = os.environ.get('AR') or 'ar' self.linker = os.environ.get('CC') or 'gcc' self.cxxlinker = os.environ.get('CXX') or 'g++' #Command definitions self.cccmd = '$toolchain$cc -MMD -MT $out -MF $out.d $includepaths $moreincludepaths $cflags $carchflags $cconfigflags $cmoreflags $cenvflags -c $in -o $out' self.cxxcmd = '$toolchain$cxx -MMD -MT $out -MF $out.d $includepaths $moreincludepaths $cxxflags $carchflags $cconfigflags $cmoreflags $cxxenvflags -c $in -o $out' self.ccdeps = 'gcc' self.ccdepfile = '$out.d' self.arcmd = self.rmcmd('$out') + ' && $toolchain$ar crsD $ararchflags $arflags $arenvflags $out $in' self.linkcmd = '$toolchain$link $libpaths $configlibpaths $linkflags $linkarchflags $linkconfigflags $linkenvflags -o $out $in $libs $archlibs $oslibs' #Base flags self.cflags = ['-D' + project.upper() + '_COMPILE=1', '-funit-at-a-time', '-fstrict-aliasing', '-fno-math-errno','-ffinite-math-only', '-funsafe-math-optimizations', '-fno-trapping-math', '-ffast-math'] self.cwarnflags = ['-Wextra', '-Wall', '-Werror'] self.cmoreflags = [] self.mflags = [] self.arflags = [] self.linkflags = [] self.oslibs = [] self.initialize_subninja(subninja) self.initialize_archs(archs) self.initialize_configs(configs) self.initialize_project(project) self.initialize_toolchain() self.initialize_depends(dependlibs) self.parse_default_variables(variables) self.read_build_prefs() if self.target.is_linux() or self.target.is_bsd() or self.target.is_raspberrypi() or self.target.is_sunos(): self.cflags += ['-D_GNU_SOURCE=1'] self.linkflags += ['-pthread'] if self.target.is_linux() or self.target.is_raspberrypi(): self.oslibs += ['dl'] if self.target.is_raspberrypi(): self.linkflags += ['-latomic'] if self.target.is_bsd(): self.oslibs += ['execinfo'] if self.target.is_haiku(): self.cflags += ['-D_GNU_SOURCE=1'] self.linkflags += ['-lpthread'] self.includepaths = self.prefix_includepaths((includepaths or []) + ['.']) if self.is_monolithic(): self.cflags += ['-DBUILD_MONOLITHIC=1'] if self.use_coverage(): self.cflags += ['--coverage'] self.linkflags += ['--coverage'] if not 'nowarning' in variables or not variables['nowarning']: self.cflags += self.cwarnflags self.cxxflags = list(self.cflags) self.cflags += ['-std=c11'] if self.target.is_macos() or self.target.is_ios(): self.cxxflags += ['-std=c++14', '-stdlib=libc++'] else: self.cxxflags += ['-std=gnu++14'] #Overrides self.objext = '.o' #Builders self.builders['c'] = self.builder_cc self.builders['cc'] = self.builder_cxx self.builders['cpp'] = self.builder_cxx self.builders['lib'] = self.builder_lib self.builders['multilib'] = self.builder_multicopy self.builders['sharedlib'] = self.builder_sharedlib self.builders['multisharedlib'] = self.builder_multicopy self.builders['bin'] = self.builder_bin self.builders['multibin'] = self.builder_multicopy #Setup target platform self.build_target_toolchain(self.target) def name(self): return 'gcc' def parse_prefs(self, prefs): super(GCCToolchain, self).parse_prefs(prefs) if 'gcc' in prefs: gccprefs = prefs['gcc'] if 'toolchain' in gccprefs: self.toolchain = gccprefs['toolchain'] if os.path.split(self.toolchain)[1] != 'bin': self.toolchain = os.path.join(self.toolchain, 'bin') def write_variables(self, writer): super(GCCToolchain, self).write_variables(writer) writer.variable('toolchain', self.toolchain) writer.variable('cc', self.ccompiler) writer.variable('cxx', self.cxxcompiler) writer.variable('ar', self.archiver) writer.variable('link', self.linker) writer.variable('includepaths', self.make_includepaths(self.includepaths)) writer.variable('moreincludepaths', '') writer.variable('cflags', self.cflags) writer.variable('cxxflags', self.cxxflags) writer.variable('carchflags', '') writer.variable('cconfigflags', '') writer.variable('cmoreflags', self.cmoreflags) writer.variable('cenvflags', (os.environ.get('CFLAGS') or '').split()) writer.variable('cxxenvflags', (os.environ.get('CXXFLAGS') or '').split()) writer.variable('arflags', self.arflags) writer.variable('ararchflags', '') writer.variable('arconfigflags', '') writer.variable('arenvflags', (os.environ.get('ARFLAGS') or '').split()) writer.variable('linkflags', self.linkflags) writer.variable('linkarchflags', '') writer.variable('linkconfigflags', '') writer.variable('linkenvflags', (os.environ.get('LDFLAGS') or '').split()) writer.variable('libs', '') writer.variable('libpaths', self.make_libpaths(self.libpaths)) writer.variable('configlibpaths', '') writer.variable('archlibs', '') writer.variable('oslibs', self.make_libs(self.oslibs)) writer.newline() def write_rules(self, writer): super(GCCToolchain, self).write_rules(writer) writer.rule('cc', command = self.cccmd, depfile = self.ccdepfile, deps = self.ccdeps, description = 'CC $in') writer.rule('cxx', command = self.cxxcmd, depfile = self.ccdepfile, deps = self.ccdeps, description = 'CXX $in') writer.rule('ar', command = self.arcmd, description = 'LIB $out') writer.rule('link', command = self.linkcmd, description = 'LINK $out') writer.rule('so', command = self.linkcmd, description = 'SO $out') writer.newline() def build_target_toolchain(self, target): if target.is_windows(): self.build_windows_toolchain() if self.toolchain != '' and not self.toolchain.endswith('/') and not self.toolchain.endswith('\\'): self.toolchain += os.sep def build_windows_toolchain(self): self.cflags += ['-U__STRICT_ANSI__'] self.oslibs = ['kernel32', 'user32', 'shell32', 'advapi32'] def make_includepath(self, path): if os.path.isabs(path) or self.subninja == '': return self.path_escape(path) if path == '.': return self.path_escape(self.subninja) return self.path_escape(os.path.join(self.subninja, path)) def make_includepaths(self, includepaths): if not includepaths is None: return ['-I' + self.make_includepath(path) for path in list(includepaths)] return [] def make_libpath(self, path): return self.path_escape(path) def make_libpaths(self, libpaths): if not libpaths is None: return ['-L' + self.make_libpath(path) for path in libpaths] return [] def make_targetarchflags(self, arch, targettype): flags = [] if arch == 'x86': flags += ['-m32'] elif arch == 'x86-64': flags += ['-m64'] return flags def make_carchflags(self, arch, targettype): flags = [] if targettype == 'sharedlib': flags += ['-DBUILD_DYNAMIC_LINK=1'] if self.target.is_linux() or self.target.is_bsd() or self.target.is_sunos(): flags += ['-fPIC'] flags += self.make_targetarchflags(arch, targettype) return flags def make_cconfigflags(self, config, targettype): flags = [] if config == 'debug': flags += ['-DBUILD_DEBUG=1', '-g'] elif config == 'release': flags += ['-DBUILD_RELEASE=1', '-O3', '-g', '-funroll-loops'] elif config == 'profile': flags += ['-DBUILD_PROFILE=1', '-O3', '-g', '-funroll-loops'] elif config == 'deploy': flags += ['-DBUILD_DEPLOY=1', '-O3', '-g', '-funroll-loops'] return flags def make_ararchflags(self, arch, targettype): flags = [] return flags def make_arconfigflags(self, config, targettype): flags = [] return flags def make_linkarchflags(self, arch, targettype): flags = [] flags += self.make_targetarchflags(arch, targettype) return flags def make_linkconfigflags(self, config, targettype): flags = [] if self.target.is_windows(): if targettype == 'sharedlib': flags += ['-Xlinker', '/DLL'] elif targettype == 'bin': flags += ['-Xlinker', '/SUBSYSTEM:CONSOLE'] elif self.target.is_macos() or self.target.is_ios(): if targettype == 'sharedlib' or targettype == 'multisharedlib': flags += ['-dynamiclib'] else: if targettype == 'sharedlib': flags += ['-shared'] return flags def make_libs(self, libs): if libs != None: return ['-l' + lib for lib in libs] return [] def make_configlibpaths(self, config, arch, extralibpaths): libpaths = [ self.libpath, os.path.join(self.libpath, arch), os.path.join(self.libpath, config), os.path.join(self.libpath, config, arch) ] if extralibpaths != None: libpaths += [os.path.join(libpath, self.libpath) for libpath in extralibpaths] libpaths += [os.path.join(libpath, self.libpath, arch) for libpath in extralibpaths] libpaths += [os.path.join(libpath, self.libpath, config) for libpath in extralibpaths] libpaths += [os.path.join(libpath, self.libpath, config, arch) for libpath in extralibpaths] return self.make_libpaths(libpaths) def cc_variables(self, config, arch, targettype, variables): localvariables = [] if 'includepaths' in variables: moreincludepaths = self.make_includepaths(variables['includepaths']) if not moreincludepaths == []: localvariables += [('moreincludepaths', moreincludepaths)] carchflags = self.make_carchflags(arch, targettype) if carchflags != []: localvariables += [('carchflags', carchflags)] cconfigflags = self.make_cconfigflags(config, targettype) if cconfigflags != []: localvariables += [('cconfigflags', cconfigflags)] if 'defines' in variables: localvariables += [('cmoreflags', ['-D' + define for define in variables['defines']])] return localvariables def ar_variables(self, config, arch, targettype, variables): localvariables = [] ararchflags = self.make_ararchflags(arch, targettype) if ararchflags != []: localvariables += [('ararchflags', ararchflags)] arconfigflags = self.make_arconfigflags(config, targettype) if arconfigflags != []: localvariables += [('arconfigflags', arconfigflags)] return localvariables def link_variables(self, config, arch, targettype, variables): localvariables = [] linkarchflags = self.make_linkarchflags(arch, targettype) if linkarchflags != []: localvariables += [('linkarchflags', linkarchflags)] linkconfigflags = self.make_linkconfigflags(config, targettype) if linkconfigflags != []: localvariables += [('linkconfigflags', linkconfigflags)] if 'libs' in variables: libvar = self.make_libs(variables['libs']) if libvar != []: localvariables += [('libs', libvar)] libpaths = [] if 'libpaths' in variables: libpaths = variables['libpaths'] localvariables += [('configlibpaths', self.make_configlibpaths(config, arch, libpaths))] if 'runtime' in variables and variables['runtime'] == 'c++': localvariables += [('link', self.cxxlinker)] return localvariables def builder_cc(self, writer, config, arch, targettype, infile, outfile, variables): return writer.build(outfile, 'cc', infile, implicit = self.implicit_deps(config, variables), variables = self.cc_variables(config, arch, targettype, variables)) def builder_cxx(self, writer, config, arch, targettype, infile, outfile, variables): return writer.build(outfile, 'cxx', infile, implicit = self.implicit_deps(config, variables), variables = self.cc_variables(config, arch, targettype, variables)) def builder_lib(self, writer, config, arch, targettype, infiles, outfile, variables): return writer.build(outfile, 'ar', infiles, implicit = self.implicit_deps(config, variables), variables = self.ar_variables(config, arch, targettype, variables)) def builder_sharedlib(self, writer, config, arch, targettype, infiles, outfile, variables): return writer.build(outfile, 'so', infiles, implicit = self.implicit_deps(config, variables), variables = self.link_variables(config, arch, targettype, variables)) def builder_bin(self, writer, config, arch, targettype, infiles, outfile, variables): return writer.build(outfile, 'link', infiles, implicit = self.implicit_deps(config, variables), variables = self.link_variables(config, arch, targettype, variables)) def create(host, target, toolchain): return GCCToolchain(host, target, toolchain)