#!/usr/bin/python

import ninja_syntax
import itertools
import os
import fnmatch
import re
import sys
import argparse

# --- util functions

def flags(*iterables):
    return ' '.join(itertools.chain(*iterables))

def include(d):
    return '-I' + d;

def dependency_include(d):
    return '-isystem' + os.path.join('deps', d, 'include');

def get_files(root, pattern):
    pattern = fnmatch.translate(pattern)
    for dir, dirs, files in os.walk(root):
        for f in files:
            if re.match(pattern, f):
                yield os.path.join(dir, f)

def object_file(fn):
    return os.path.join('obj', re.sub(r'\.c\+\+$', '.o', fn))

# --- arguments

parser = argparse.ArgumentParser()
parser.add_argument('--cxx', default='g++', metavar='executable', help='compiler name to use (default: g++)')
parser.add_argument('--boost-dir', default=None, metavar='path', help='path of boost include files')
args = parser.parse_args()

# --- variables

dependencies = ['catch', 'wheels']
include_flags = flags([include('include')], map(dependency_include, dependencies))
if(args.boost_dir):
    include_flags += ' ' + include(args.boost_dir)
cxx_flags = flags(['-Wall', '-Wextra', '-Werror', '-std=c++11', '-O3'])
ld_flags = flags(['-flto'])

# --- preamble

ninja = ninja_syntax.Writer(open('build.ninja', 'w'))

ninja.variable('ninja_required_version', '1.3')
ninja.variable('builddir', 'obj' + os.sep)

# --- rules

ninja.rule('bootstrap',
        command = ' '.join(sys.argv),
        generator = True,
        description = 'BOOTSTRAP')

ninja.rule('cxx',
        command = args.cxx + ' -MMD -MF $out.d -c ' + cxx_flags + ' ' + include_flags + ' $in -o $out',
        deps = 'gcc',
        depfile = '$out.d',
        description = 'C++ $in')

ninja.rule('link',
        command = args.cxx + ' ' + cxx_flags + ' ' + ld_flags + ' $in -o $out',
        description = 'LINK $in')

ninja.rule('lib',
        command = 'ar rcs $out $in',
        description = 'AR $in')

ninja.rule('dist',
        command = 'tar cjf $out $in',
        description = 'TAR $in')

# --- build edges

ninja.build('build.ninja', 'bootstrap',
        implicit = sys.argv[0])

hdr_files = list(get_files('include', '*.h++'))
src_files = list(get_files('src', '*.c++'))
obj_files = [object_file(fn) for fn in src_files]
for fn in src_files:
    ninja.build(object_file(fn), 'cxx',
            inputs = fn)

libnonius = os.path.join('bin', 'libnonius.a')
ninja.build(libnonius, 'lib',
        inputs = obj_files)

test_src_files = list(get_files('test', '*.c++'))
test_obj_files = [object_file(fn) for fn in test_src_files]
for fn in test_src_files:
    ninja.build(object_file(fn), 'cxx',
            inputs = fn)

test_runner = os.path.join('bin', 'test')
ninja.build(test_runner, 'link',
        inputs = test_obj_files + [libnonius])
ninja.build('test', 'phony',
        inputs = test_runner)
ninja.build('lib', 'phony',
        inputs = libnonius)

tar = os.path.join('dist', 'nonius.tar.bz2')
ninja.build(tar, 'dist',
        inputs = hdr_files + ([libnonius] if src_files else []))

ninja.build('dist', 'phony',
        inputs = tar)

# --- examples

example_files = list(get_files('examples', '*.c++'))
examples = []
for fn in example_files:
    ninja.build(object_file(fn), 'cxx',
            inputs = fn)
    name = re.sub(r'\.c\+\+$', '', os.path.basename(fn))
    example = os.path.join('bin', 'examples', name)
    ninja.build(example, 'link',
            inputs = [object_file(fn), libnonius])
    ninja.build(name, 'phony',
            inputs = example)
    examples += [name]

ninja.build('examples', 'phony',
            inputs = examples)

# --- default targets

ninja.default('lib')


@@ -899,16 +899,115 @@
 files')%0A
+parser.add_argument('--no-lto', action='store_true', help='do not perform link-time optimisation')%0A
 args = p
@@ -1306,32 +1306,55 @@
 O3'%5D)%0Ald_flags =
+ '' if args.no_lto else
  flags(%5B'-flto'%5D

Add --no-lto to bootstrap script