aboutsummaryrefslogtreecommitdiff
path: root/tests/kuroga.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/kuroga.py')
-rwxr-xr-xtests/kuroga.py312
1 files changed, 312 insertions, 0 deletions
diff --git a/tests/kuroga.py b/tests/kuroga.py
new file mode 100755
index 0000000..56d3f86
--- /dev/null
+++ b/tests/kuroga.py
@@ -0,0 +1,312 @@
+#!/usr/bin/env python
+
+#
+# Kuroga, single python file meta-build system for ninja
+# https://github.com/lighttransport/kuroga
+#
+# Requirements: python 2.6 or 2.7
+#
+# Usage: $ python kuroga.py input.py
+#
+
+import imp
+import re
+import textwrap
+import glob
+import os
+import sys
+
+# gcc preset
+def add_gnu_rule(ninja):
+ ninja.rule('gnucxx', description='CXX $out',
+ command='$gnucxx -MMD -MF $out.d $gnudefines $gnuincludes $gnucxxflags -c $in -o $out',
+ depfile='$out.d', deps='gcc')
+ ninja.rule('gnucc', description='CC $out',
+ command='$gnucc -MMD -MF $out.d $gnudefines $gnuincludes $gnucflags -c $in -o $out',
+ depfile='$out.d', deps='gcc')
+ ninja.rule('gnulink', description='LINK $out', pool='link_pool',
+ command='$gnuld -o $out $in $libs $gnuldflags')
+ ninja.rule('gnuar', description='AR $out', pool='link_pool',
+ command='$gnuar rsc $out $in')
+ ninja.rule('gnustamp', description='STAMP $out', command='touch $out')
+ ninja.newline()
+
+ ninja.variable('gnucxx', 'g++')
+ ninja.variable('gnucc', 'gcc')
+ ninja.variable('gnuld', '$gnucxx')
+ ninja.variable('gnuar', 'ar')
+ ninja.newline()
+
+# clang preset
+def add_clang_rule(ninja):
+ ninja.rule('clangcxx', description='CXX $out',
+ command='$clangcxx -MMD -MF $out.d $clangdefines $clangincludes $clangcxxflags -c $in -o $out',
+ depfile='$out.d', deps='gcc')
+ ninja.rule('clangcc', description='CC $out',
+ command='$clangcc -MMD -MF $out.d $clangdefines $clangincludes $clangcflags -c $in -o $out',
+ depfile='$out.d', deps='gcc')
+ ninja.rule('clanglink', description='LINK $out', pool='link_pool',
+ command='$clangld -o $out $in $libs $clangldflags')
+ ninja.rule('clangar', description='AR $out', pool='link_pool',
+ command='$clangar rsc $out $in')
+ ninja.rule('clangstamp', description='STAMP $out', command='touch $out')
+ ninja.newline()
+
+ ninja.variable('clangcxx', 'clang++')
+ ninja.variable('clangcc', 'clang')
+ ninja.variable('clangld', '$clangcxx')
+ ninja.variable('clangar', 'ar')
+ ninja.newline()
+
+# msvc preset
+def add_msvc_rule(ninja):
+ ninja.rule('msvccxx', description='CXX $out',
+ command='$msvccxx /TP /showIncludes $msvcdefines $msvcincludes $msvccxxflags -c $in /Fo$out',
+ depfile='$out.d', deps='msvc')
+ ninja.rule('msvccc', description='CC $out',
+ command='$msvccc /TC /showIncludes $msvcdefines $msvcincludes $msvccflags -c $in /Fo$out',
+ depfile='$out.d', deps='msvc')
+ ninja.rule('msvclink', description='LINK $out', pool='link_pool',
+ command='$msvcld $msvcldflags $in $libs /OUT:$out')
+ ninja.rule('msvcar', description='AR $out', pool='link_pool',
+ command='$msvcar $in /OUT:$out')
+ #ninja.rule('msvcstamp', description='STAMP $out', command='touch $out')
+ ninja.newline()
+
+ ninja.variable('msvccxx', 'cl.exe')
+ ninja.variable('msvccc', 'cl.exe')
+ ninja.variable('msvcld', 'link.exe')
+ ninja.variable('msvcar', 'lib.exe')
+ ninja.newline()
+
+# -- from ninja_syntax.py --
+def escape_path(word):
+ return word.replace('$ ', '$$ ').replace(' ', '$ ').replace(':', '$:')
+
+class Writer(object):
+ def __init__(self, output, width=78):
+ self.output = output
+ self.width = width
+
+ def newline(self):
+ self.output.write('\n')
+
+ def comment(self, text, has_path=False):
+ for line in textwrap.wrap(text, self.width - 2, break_long_words=False,
+ break_on_hyphens=False):
+ self.output.write('# ' + line + '\n')
+
+ def variable(self, key, value, indent=0):
+ if value is None:
+ return
+ if isinstance(value, list):
+ value = ' '.join(filter(None, value)) # Filter out empty strings.
+ self._line('%s = %s' % (key, value), indent)
+
+ def pool(self, name, depth):
+ self._line('pool %s' % name)
+ self.variable('depth', depth, indent=1)
+
+ def rule(self, name, command, description=None, depfile=None,
+ generator=False, pool=None, restat=False, rspfile=None,
+ rspfile_content=None, deps=None):
+ self._line('rule %s' % name)
+ self.variable('command', command, indent=1)
+ if description:
+ self.variable('description', description, indent=1)
+ if depfile:
+ self.variable('depfile', depfile, indent=1)
+ if generator:
+ self.variable('generator', '1', indent=1)
+ if pool:
+ self.variable('pool', pool, indent=1)
+ if restat:
+ self.variable('restat', '1', indent=1)
+ if rspfile:
+ self.variable('rspfile', rspfile, indent=1)
+ if rspfile_content:
+ self.variable('rspfile_content', rspfile_content, indent=1)
+ if deps:
+ self.variable('deps', deps, indent=1)
+
+ def build(self, outputs, rule, inputs=None, implicit=None, order_only=None,
+ variables=None):
+ outputs = as_list(outputs)
+ out_outputs = [escape_path(x) for x in outputs]
+ all_inputs = [escape_path(x) for x in as_list(inputs)]
+
+ if implicit:
+ implicit = [escape_path(x) for x in as_list(implicit)]
+ all_inputs.append('|')
+ all_inputs.extend(implicit)
+ if order_only:
+ order_only = [escape_path(x) for x in as_list(order_only)]
+ all_inputs.append('||')
+ all_inputs.extend(order_only)
+
+ self._line('build %s: %s' % (' '.join(out_outputs),
+ ' '.join([rule] + all_inputs)))
+
+ if variables:
+ if isinstance(variables, dict):
+ iterator = iter(variables.items())
+ else:
+ iterator = iter(variables)
+
+ for key, val in iterator:
+ self.variable(key, val, indent=1)
+
+ return outputs
+
+ def include(self, path):
+ self._line('include %s' % path)
+
+ def subninja(self, path):
+ self._line('subninja %s' % path)
+
+ def default(self, paths):
+ self._line('default %s' % ' '.join(as_list(paths)))
+
+ def _count_dollars_before_index(self, s, i):
+ """Returns the number of '$' characters right in front of s[i]."""
+ dollar_count = 0
+ dollar_index = i - 1
+ while dollar_index > 0 and s[dollar_index] == '$':
+ dollar_count += 1
+ dollar_index -= 1
+ return dollar_count
+
+ def _line(self, text, indent=0):
+ """Write 'text' word-wrapped at self.width characters."""
+ leading_space = ' ' * indent
+ while len(leading_space) + len(text) > self.width:
+ # The text is too wide; wrap if possible.
+
+ # Find the rightmost space that would obey our width constraint and
+ # that's not an escaped space.
+ available_space = self.width - len(leading_space) - len(' $')
+ space = available_space
+ while True:
+ space = text.rfind(' ', 0, space)
+ if (space < 0 or
+ self._count_dollars_before_index(text, space) % 2 == 0):
+ break
+
+ if space < 0:
+ # No such space; just use the first unescaped space we can find.
+ space = available_space - 1
+ while True:
+ space = text.find(' ', space + 1)
+ if (space < 0 or
+ self._count_dollars_before_index(text, space) % 2 == 0):
+ break
+ if space < 0:
+ # Give up on breaking.
+ break
+
+ self.output.write(leading_space + text[0:space] + ' $\n')
+ text = text[space+1:]
+
+ # Subsequent lines are continuations, so indent them.
+ leading_space = ' ' * (indent+2)
+
+ self.output.write(leading_space + text + '\n')
+
+ def close(self):
+ self.output.close()
+
+
+def as_list(input):
+ if input is None:
+ return []
+ if isinstance(input, list):
+ return input
+ return [input]
+
+# -- end from ninja_syntax.py --
+
+def gen(ninja, toolchain, config):
+
+ ninja.variable('ninja_required_version', '1.4')
+ ninja.newline()
+
+ if hasattr(config, "builddir"):
+ builddir = config.builddir[toolchain]
+ ninja.variable(toolchain + 'builddir', builddir)
+ else:
+ builddir = ''
+
+ ninja.variable(toolchain + 'defines', config.defines[toolchain] or [])
+ ninja.variable(toolchain + 'includes', config.includes[toolchain] or [])
+ ninja.variable(toolchain + 'cflags', config.cflags[toolchain] or [])
+ ninja.variable(toolchain + 'cxxflags', config.cxxflags[toolchain] or [])
+ ninja.variable(toolchain + 'ldflags', config.ldflags[toolchain] or [])
+ ninja.newline()
+
+ if hasattr(config, "link_pool_depth"):
+ ninja.pool('link_pool', depth=config.link_pool_depth)
+ else:
+ ninja.pool('link_pool', depth=4)
+ ninja.newline()
+
+ # Add default toolchain(gnu, clang and msvc)
+ add_gnu_rule(ninja)
+ add_clang_rule(ninja)
+ add_msvc_rule(ninja)
+
+ obj_files = []
+
+ cc = toolchain + 'cc'
+ cxx = toolchain + 'cxx'
+ link = toolchain + 'link'
+ ar = toolchain + 'ar'
+
+ if hasattr(config, "cxx_files"):
+ for src in config.cxx_files:
+ srcfile = src
+ obj = os.path.splitext(srcfile)[0] + '.o'
+ obj = os.path.join(builddir, obj);
+ obj_files.append(obj)
+ ninja.build(obj, cxx, srcfile)
+ ninja.newline()
+
+ if hasattr(config, "c_files"):
+ for src in config.c_files:
+ srcfile = src
+ obj = os.path.splitext(srcfile)[0] + '.o'
+ obj = os.path.join(builddir, obj);
+ obj_files.append(obj)
+ ninja.build(obj, cc, srcfile)
+ ninja.newline()
+
+ targetlist = []
+ if hasattr(config, "exe"):
+ ninja.build(config.exe, link, obj_files)
+ targetlist.append(config.exe)
+
+ if hasattr(config, "staticlib"):
+ ninja.build(config.staticlib, ar, obj_files)
+ targetlist.append(config.staticlib)
+
+ ninja.build('all', 'phony', targetlist)
+ ninja.newline()
+
+ ninja.default('all')
+
+def main():
+ if len(sys.argv) < 2:
+ print("Usage: python kuroga.py config.py")
+ sys.exit(1)
+
+ config = imp.load_source("config", sys.argv[1])
+
+ f = open('build.ninja', 'w')
+ ninja = Writer(f)
+
+ if hasattr(config, "register_toolchain"):
+ config.register_toolchain(ninja)
+
+ gen(ninja, config.toolchain, config)
+ f.close()
+
+main()