From 638c204932199604fce6b37ebc07f5f070b156ff Mon Sep 17 00:00:00 2001
From: Campbell Barton <ideasman42@gmail.com>
Date: Wed, 23 Sep 2015 01:39:10 +1000
Subject: [PATCH] Add cmake utility to configure compiler flags

---
 utils_build/cmake-flags | 389 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 389 insertions(+)
 create mode 100755 utils_build/cmake-flags

diff --git a/utils_build/cmake-flags b/utils_build/cmake-flags
new file mode 100755
index 0000000..ebc371d
--- /dev/null
+++ b/utils_build/cmake-flags
@@ -0,0 +1,389 @@
+#!/usr/bin/env python3
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+"""
+This tool is for configuring build flags.
+"""
+
+WITH_GUI = True
+
+# ----------------------------------------------------------------------------
+# Data (flags)
+# eg: indervidual flags, compat info.
+
+
+# ----------------------------------------------------------------------------
+# Data (presets)
+# eg: profiling, mudflap, debugging.
+
+# setting: ((add, ...), (remove, ...)), ...
+PRESETS = {
+    "sanitize_address": {
+        "CMAKE_CXX_FLAGS": (("-fsanitize=address",), ()),
+        "CMAKE_C_FLAGS":   (("-fsanitize=address",), ()),
+        "CMAKE_EXE_LINKER_FLAGS": (("-lasan",), ()),
+        },
+    "sanitize_undefined": {
+        "CMAKE_CXX_FLAGS": (("-fsanitize=undefined",), ()),
+        "CMAKE_C_FLAGS":   (("-fsanitize=undefined",), ()),
+        },
+    "sanitize_thread": {
+        "CMAKE_CXX_FLAGS": (("-fsanitize=thread",), ()),
+        "CMAKE_C_FLAGS":   (("-fsanitize=thread",), ()),
+        },
+
+    # GCC5
+    "sanitize_float_divide_by_zero": {
+        "CMAKE_CXX_FLAGS": (("-fsanitize=float-divide-by-zero",), ()),
+        "CMAKE_C_FLAGS":   (("-fsanitize=float-divide-by-zero",), ()),
+        },
+    "sanitize_float_cast_overflow": {
+        "CMAKE_CXX_FLAGS": (("-fsanitize=float-cast-overflow",), ()),
+        "CMAKE_C_FLAGS":   (("-fsanitize=float-cast-overflow",), ()),
+        },
+    "sanitize_bounds": {
+        "CMAKE_CXX_FLAGS": (("-fsanitize=bounds",), ()),
+        "CMAKE_C_FLAGS":   (("-fsanitize=bounds",), ()),
+        },
+    "sanitize_alignment": {
+        "CMAKE_CXX_FLAGS": (("-fsanitize=alignment",), ()),
+        "CMAKE_C_FLAGS":   (("-fsanitize=alignment",), ()),
+        },
+    "sanitize_object_size": {
+        "CMAKE_CXX_FLAGS": (("-fsanitize=object-size",), ()),
+        "CMAKE_C_FLAGS":   (("-fsanitize=object-size",), ()),
+        },
+
+    "warn_all": {
+        "CMAKE_CXX_FLAGS": (("-Wall",), ()),
+        "CMAKE_C_FLAGS":   (("-Wall",), ()),
+        },
+    "warn_extra": {
+        "CMAKE_CXX_FLAGS": (("-Wextra",), ()),
+        "CMAKE_C_FLAGS":   (("-Wextra",), ()),
+        },
+    "warn_unused_macros": {
+        "CMAKE_CXX_FLAGS": (("-Wunused-macros",), ()),
+        "CMAKE_C_FLAGS":   (("-Wunused-macros",), ()),
+        },
+    "warn_undefined_macros": {
+        "CMAKE_CXX_FLAGS": (("-Wundef",), ()),
+        "CMAKE_C_FLAGS":   (("-Wundef",), ()),
+        },
+    "warn_unused_local_typedefs": {
+        "CMAKE_CXX_FLAGS": (("-Wunused-local-typedefs",), ()),
+        "CMAKE_C_FLAGS":   (("-Wunused-local-typedefs",), ()),
+        },
+    "warn_pointer_sign": {
+        "CMAKE_CXX_FLAGS": (("",), ()),
+        "CMAKE_C_FLAGS":   (("-Wpointer-sign",), ()),
+        },
+    "warn_sizeof_pointer_memaccess": {
+        "CMAKE_CXX_FLAGS": (("-Wsizeof-pointer-memaccess",), ()),
+        "CMAKE_C_FLAGS":   (("-Wsizeof-pointer-memaccess",), ()),
+        },
+    "warn_no_null": {
+        "CMAKE_CXX_FLAGS": (("-Wnonnull",), ()),
+        "CMAKE_C_FLAGS":   (("-Wnonnull",), ()),
+        },
+    "warn_init_self": {
+        "CMAKE_CXX_FLAGS": (("-Winit-self",), ()),
+        "CMAKE_C_FLAGS":   (("-Winit-self",), ()),
+        },
+    "warn_format": {
+        "CMAKE_CXX_FLAGS": (("-Wformat=2", "-Wno-format-nonliteral", "-Wno-format-y2k"), ()),
+        "CMAKE_C_FLAGS":   (("-Wformat=2", "-Wno-format-nonliteral", "-Wno-format-y2k"), ()),
+        },
+    "warn_format": {
+        "CMAKE_CXX_FLAGS": (("-Wwrite-strings",), ()),
+        "CMAKE_C_FLAGS":   (("-Wwrite-strings",), ()),
+        },
+    "warn_logical_op": {
+        "CMAKE_CXX_FLAGS": (("-Wlogical-op",), ()),
+        "CMAKE_C_FLAGS":   (("-Wlogical-op",), ()),
+        },
+    "warn_error": {
+        "CMAKE_CXX_FLAGS": (("-Werror",), ()),
+        "CMAKE_C_FLAGS":   (("-Werror",), ()),
+        },
+    "warn_shadow": {
+        "CMAKE_CXX_FLAGS": (("-Wshadow", "-Wno-error=shadow"), ()),
+        "CMAKE_C_FLAGS":   (("-Wshadow", "-Wno-error=shadow"), ()),
+        },
+    "warn_missing_include_dirs": {
+        "CMAKE_CXX_FLAGS": (("-Wmissing-include-dirs",), ()),
+        "CMAKE_C_FLAGS":   (("-Wmissing-include-dirs",), ()),
+        },
+    "warn_double_promotion": {
+        "CMAKE_CXX_FLAGS": (("-Wdouble-promotion",), ()),
+        "CMAKE_C_FLAGS":   (("-Wdouble-promotion",), ()),
+        },
+    "warn_declaration_after_statement": {
+        "CMAKE_C_FLAGS":   (("-Wdeclaration-after-statement",), ()),
+        },
+    "warn_zero_as_null_pointer_constant": {
+        "CMAKE_CXX_FLAGS": (("-Wzero-as-null-pointer-constant",), ()),
+        },
+    "show_color": {
+        "CMAKE_C_FLAGS": (("-fdiagnostics-color=always",), ()),
+        "CMAKE_CXX_FLAGS": (("-fdiagnostics-color=always",), ()),
+        },
+
+    # Optimize
+    "optimize_whole_program": {
+        "CMAKE_CXX_FLAGS": (("-flto",), ()),
+        "CMAKE_C_FLAGS":   (("-flto",), ()),
+        "CMAKE_EXE_LINKER_FLAGS": (("-flto", "-fwhole-program",), ()),
+        },
+}
+
+# ----------------------------------------------------------------------------
+# Utility Functions
+# eg: check buildsystem (make or ninja?)
+
+
+def cmake_flag_buildtype_suffix(flag, build_type):
+    """
+    Add the build type as a suffix for options that support it.
+    this way for Debug builds we edit debug flags.
+    eg:
+      CMAKE_CXX_FLAGS -> CMAKE_CXX_FLAGS_DEBUG
+    """
+    if build_type == "":
+        return flag
+
+    # perhaps there are more,
+    # but these are default that can have _DEBUG... etc added.
+    if flag in {'CMAKE_CXX_FLAGS',
+                'CMAKE_C_FLAGS',
+                'CMAKE_EXE_LINKER_FLAGS',
+                'CMAKE_MODULE_LINKER_FLAGS',
+                'CMAKE_SHARED_LINKER_FLAGS'}:
+
+        return "%s_%s" % (flag, build_type.upper())
+    else:
+        return flag
+
+
+# ----------------------------------------------------------------------------
+# CMakeCache.txt Parser (simple)
+#
+# These functions should run standalone
+# format in python is as follows...
+#
+# CMakeCache.txt is converted into a dict
+# the key is the cache ID
+# the value is a triple (type, value, description, internal)
+# where the discription is the comment above conforming to the CMake convention.
+#
+#
+def cmakecache_to_py(filepath, native=True):
+    """
+    header, cache
+    """
+
+    cmake_header = ""
+    cmake_cache = {}
+
+    with open(filepath, 'r', encoding='utf-8') as f:
+        lines = f.readlines()
+        for i in range(len(lines)):
+            if lines[i].startswith("#"):
+                cmake_header += lines[i]
+            else:
+                break
+
+        # incase its not set
+        cmake_descr = ""
+        cmake_internal = False
+
+        for i in range(len(lines)):
+            if lines[i].startswith("//"):
+                cmake_descr += lines[i][2:].rstrip() + "\n"
+            elif lines[i].startswith("#"):
+                if "INTERNAL cache entries" in lines[i]:
+                    cmake_internal = True
+            elif lines[i][0].isalpha() or lines[i][0] in {"_", "-"}:
+                cmake_name, cmake_value = lines[i].split("=", 1)
+                if ":" in cmake_name:
+                    cmake_name, cmake_type = cmake_name.split(":", 1)
+                else:
+                    cmake_type = "STRING"
+                cmake_value = cmake_value.rstrip()  # remove trailing '\n'
+                cmake_descr = cmake_descr.rstrip()
+
+                if native:
+                    if cmake_type in {"STRING", "PATH", "FILEPATH", "STATIC", "INTERNAL", "UNINITIALIZED"}:
+                        pass
+                    elif cmake_type == "BOOL":
+                        cmake_value = cmake_value.upper() not in {"NO", "N", "", "OFF", "0", "FALSE"}
+
+                cmake_cache[cmake_name] = (cmake_type, cmake_value, cmake_descr, cmake_internal)
+
+                # incase its not set
+                cmake_descr = ""
+
+    return cmake_header, cmake_cache
+
+
+def cmakecache_from_py(filepath, cmake_header, cmake_cache):
+
+    with open(filepath, 'w', encoding='utf-8') as f:
+        f.write(cmake_header)
+        f.write("\n")
+
+        cmake_cache_list = ([], [])
+
+        # sort into external/internal
+        for cmake_name, (cmake_type, cmake_value, cmake_descr, cmake_internal) in cmake_cache.items():
+            cmake_cache_list[cmake_internal].append((cmake_name, cmake_type, cmake_value, cmake_descr))
+
+        for is_internal, ls in enumerate(cmake_cache_list):
+            ls.sort()
+            if is_internal:
+                f.write("########################\n"
+                        "# INTERNAL cache entries\n"
+                        "########################\n\n")
+            else:
+                f.write("########################\n"
+                        "# EXTERNAL cache entries\n"
+                        "########################\n\n")
+
+            for cmake_name, cmake_type, cmake_value, cmake_descr in ls:
+                if cmake_descr:
+                    l = None
+                    for l in cmake_descr.split("\n"):
+                        f.write("//%s\n" % l)
+                    del l
+
+                # convert the value
+                if cmake_value is True:
+                    cmake_value = "TRUE"
+                elif cmake_value is False:
+                    cmake_value = "FALSE"
+
+                f.write("%s:%s=%s\n" % (cmake_name, cmake_type, cmake_value))
+                if not is_internal:
+                    f.write("\n")
+
+# cmake_header, cmake_cache = cmakecache_to_py("/src/cmake_debug/CMakeCache.txt~")
+# cmakecache_from_py("/src/cmake_debug/CMakeCache.txt", cmake_header, cmake_cache)
+# print(cmake_cache)
+
+
+# ----------------------------------------------------------------------------
+# Main Functions (can be run from command line)
+
+def config_set(config_id, state):
+    print(config_id, state.get(), dir(state))
+    cache = CMAKE_DATA["cmake_cache"]
+    build_type = cache["CMAKE_BUILD_TYPE"][1]  # value
+
+    cfg = PRESETS[config_id]
+    for key, (add, rem) in cfg.items():
+        key = cmake_flag_buildtype_suffix(key, build_type)
+        (cmake_type, cmake_value, cmake_descr, cmake_internal) = cache[key]
+        data = cmake_value.split()
+        if not state.get():
+            add, rem = rem, add
+
+        for arg in rem:
+            data[:] = [i for i in data if i != arg]
+        data.extend(add)
+
+        # print("A", data)
+        cmake_value = " ".join(data)
+        # print("B", cmake_value)
+        cache[key] = (cmake_type, cmake_value, cmake_descr, cmake_internal)
+        print("AFTER", cache[key])
+
+
+def config_check(config_id):
+    cache = CMAKE_DATA["cmake_cache"]
+    build_type = cache["CMAKE_BUILD_TYPE"][1]  # value
+
+    cfg = PRESETS[config_id]
+    for key, (add, rem) in cfg.items():
+        key = cmake_flag_buildtype_suffix(key, build_type)
+        (cmake_type, cmake_value, cmake_descr, cmake_internal) = cache[key]
+        data = cmake_value.split()
+        for arg in add:
+            if arg not in data:
+                return False
+    return True
+
+
+# ----------------------------------------------------------------------------
+# Command Line Interface
+
+# ----------------------------------------------------------------------------
+# User Interface
+if WITH_GUI:
+    CMAKE_DATA = {}
+    # CMAKE_CACHE = "/src/cmake_debugCMakeCache.txt"
+    CMAKE_CACHE = "CMakeCache.txt"
+
+    def cmake_read():
+        print("%s: reading..." % CMAKE_CACHE)
+        (CMAKE_DATA["cmake_header"],
+         CMAKE_DATA["cmake_cache"],
+         ) = cmakecache_to_py(CMAKE_CACHE)
+
+    def cmake_write():
+        print("%s: writing..." % CMAKE_CACHE)
+        CMAKE_CACHE_TMP = CMAKE_CACHE + "~"
+        cmakecache_from_py(CMAKE_CACHE_TMP,
+                           CMAKE_DATA["cmake_header"],
+                           CMAKE_DATA["cmake_cache"])
+
+        import shutil
+        shutil.move(CMAKE_CACHE_TMP, CMAKE_CACHE)
+
+    # read before load.
+    cmake_read()
+
+    import tkinter
+    master = tkinter.Tk()
+
+    row = 0
+    tkinter.Label(master, text="Flags:").grid(row=row, sticky=tkinter.W)
+    row += 1
+
+    def config_but(my_id):
+        global row
+        var = tkinter.IntVar()
+        var.set(config_check(my_id))
+        tkinter.Checkbutton(master,
+                            text=my_id.replace("_", " ").capitalize(),
+                            variable=var,
+                            command=lambda: config_set(my_id, var)).grid(row=row, sticky=tkinter.W)
+        row += 1
+
+    for my_id in sorted(PRESETS.keys()):
+        config_but(my_id)
+
+    tkinter.Button(master, text='Write', command=cmake_write).grid(row=row, sticky=tkinter.W, pady=4)
+    row += 1
+    tkinter.Button(master, text='Quit', command=master.quit).grid(row=row, sticky=tkinter.W, pady=4)
+    row += 1
+    # tkinter.Button(master, text='Show', command=var_states).grid(row=4, sticky=tkinter.W, pady=4)
+
+    tkinter.mainloop()
+
+# EOF
-- 
GitLab