1#!/usr/bin/python3
2import os
3import sys
4import fnmatch
5
6
7__doc__ = """Build configurator for FLIF.
8
9Usage:
10    configure.py [options] [-D var]...
11
12Options:
13  --native           Enable native optimizations.
14  -d, --debug        Compile with debug.
15  -D var             Define variable. Also -D EXAMPLE=1.
16  -o file            Output ninja file [default: build.ninja].
17  -h, --help         Show this screen.
18"""
19
20def find_objects(n, dirpaths):
21    objects = []
22    for dirpath in dirpaths:
23        for root, dirnames, filenames in os.walk(dirpath):
24            for filename in fnmatch.filter(filenames, "*.cpp"):
25                if "flif-interface" in filename:
26                    continue
27                src = os.path.join(root, filename)
28                obj = os.path.join("obj", os.path.splitext(src)[0] + ".o")
29                n.build(obj, "cxx", src)
30                objects.append(obj)
31    return objects
32
33def write_ninja(n, args):
34    ########################
35    # Generic build rules. #
36    ########################
37    exe_ext = ".exe" if os.name == "nt" else ""
38    n.variable("cxx", "g++")
39
40    if args["--debug"]:
41        n.variable("dbgflags", "-ggdb")
42        debug_flag = ""
43    else:
44        n.variable("dbgflags", "")
45        debug_flag = " -DNDEBUG"
46
47    native_flag = " -march=native" if args["--native"] else ""
48    n.variable("optflags", "-O2 -ftree-vectorize" + native_flag + debug_flag)
49
50    defines = [" -D" + d for d in args["-D"]]
51    cxxflags = "-std=c++11 -Wall -pedantic `pkg-config --cflags zlib libpng`"
52    n.variable("cxxflags", cxxflags + "".join(defines))
53    n.variable("cxxlinkflags", "`pkg-config --libs libpng`")
54
55    n.rule("cxx",
56           "$cxx $xtype -MMD -MF $out.d $optflags $dbgflags $cxxflags -c $in -o $out",
57           depfile="$out.d")
58
59    n.rule("cxxlink", "$cxx $optflags $dbgflags $in $cxxlinkflags -o $out")
60
61    ###################
62    # Build commands. #
63    ###################
64    objects = find_objects(n, ["extern", "src"])
65    n.build("src/flif" + exe_ext, "cxxlink", objects)
66    n.default("src/flif" + exe_ext)
67
68
69try:
70    from docopt import docopt
71    import ninja_syntax
72except ImportError:
73    msg = """You are missing one or more dependencies, install using:
74
75    {} -m pip install --user docopt ninja-syntax
76
77If you are using an old version of Python, and don't have pip,
78download https://bootstrap.pypa.io/get-pip.py and run it."""
79
80    print(msg.format(os.path.basename(sys.executable)))
81    sys.exit(1)
82
83
84args = docopt(__doc__)
85
86# Check if we're likely running for the first time.
87first_time = not os.path.isfile(args["-o"])
88
89# Write ninja build file.
90with open(args["-o"], "w") as ninja_file:
91    n = ninja_syntax.Writer(ninja_file)
92    write_ninja(n, args)
93
94# Check if we have ninja in path.
95path = os.environ.get("PATH", os.defpath).split(os.pathsep)
96if sys.platform == "win32":
97    path.insert(0, os.curdir)
98    pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
99    files = ["ninja" + ext for ext in pathext]
100else:
101    files = ["ninja"]
102
103is_exe = lambda p: os.path.exists(p) and os.access(p, os.F_OK | os.X_OK) and not os.path.isdir(p)
104has_ninja = any(is_exe(os.path.join(d, f)) for d in path for f in files)
105
106if first_time:
107    if not has_ninja:
108        msg = """It appears you're running configure.py for the first time, but do not have
109ninja in your path. On Windows we recommend simply downloading the binary:
110
111    https://github.com/ninja-build/ninja/releases/download/v1.6.0/ninja-win.zip
112
113Extract anywhere in your path, or even inside this directory.
114
115On linux it's easiest to compile from source:
116
117    wget https://github.com/ninja-build/ninja/archive/v1.6.0.tar.gz
118    tar xf v1.6.0.tar.gz && rm v1.6.0.tar.gz && cd ninja-1.6.0
119    {} configure.py --bootstrap
120    sudo cp ninja /usr/local/bin
121
122This should only take half a minute or so."""
123        print(msg.format(os.path.basename(sys.executable)))
124    else:
125        print("Type 'ninja' to build FLIF.")
126