429 lines
14 KiB
Python
Executable File
429 lines
14 KiB
Python
Executable File
#!/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_leak": {
|
|
"CMAKE_CXX_FLAGS": (("-fsanitize=leak",), ()),
|
|
"CMAKE_C_FLAGS": (("-fsanitize=leak",), ()),
|
|
},
|
|
"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_int_overflow": {
|
|
"CMAKE_CXX_FLAGS": (("-fsanitize=signed-integer-overflow",), ()),
|
|
"CMAKE_C_FLAGS": (("-fsanitize=signed-integer-overflow",), ()),
|
|
},
|
|
"sanitize_bool": {
|
|
"CMAKE_CXX_FLAGS": (("-fsanitize=bool",), ()),
|
|
"CMAKE_C_FLAGS": (("-fsanitize=bool",), ()),
|
|
},
|
|
"sanitize_enum": {
|
|
"CMAKE_CXX_FLAGS": (("-fsanitize=enum",), ()),
|
|
"CMAKE_C_FLAGS": (("-fsanitize=enum",), ()),
|
|
},
|
|
"sanitize_bounds": {
|
|
"CMAKE_CXX_FLAGS": (("-fsanitize=bounds",), ()),
|
|
"CMAKE_C_FLAGS": (("-fsanitize=bounds",), ()),
|
|
},
|
|
"sanitize_bounds_strict": {
|
|
"CMAKE_CXX_FLAGS": (("-fsanitize=bounds-strict",), ()),
|
|
"CMAKE_C_FLAGS": (("-fsanitize=bounds-strict",), ()),
|
|
},
|
|
"sanitize_vla_bound": {
|
|
"CMAKE_CXX_FLAGS": (("-fsanitize=vla-bound",), ()),
|
|
"CMAKE_C_FLAGS": (("-fsanitize=vla-bound",), ()),
|
|
},
|
|
"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",), ()),
|
|
},
|
|
"sanitize_nonull_attribute": {
|
|
"CMAKE_CXX_FLAGS": (("-fsanitize=nonnull-attribute",), ()),
|
|
"CMAKE_C_FLAGS": (("-fsanitize=nonnull-attribute",), ()),
|
|
},
|
|
"sanitize_returns_nonull_attribute": {
|
|
"CMAKE_CXX_FLAGS": (("-fsanitize=returns-nonnull-attribute",), ()),
|
|
"CMAKE_C_FLAGS": (("-fsanitize=returns-nonnull-attribute",), ()),
|
|
},
|
|
|
|
"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",), ()),
|
|
},
|
|
|
|
# Profile
|
|
"profile_gprof": {
|
|
"CMAKE_CXX_FLAGS": (("-pg",), ()),
|
|
"CMAKE_C_FLAGS": (("-pg",), ()),
|
|
"CMAKE_EXE_LINKER_FLAGS": (("-pg",), ()),
|
|
},
|
|
}
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# 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 description 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
|
|
|
|
# in case it's 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)
|
|
|
|
# in case it's 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
|