Skip to content

Commit 2b757fa

Browse files
committed
build: bootstrap debug presets with release dependencies
1 parent b997dec commit 2b757fa

File tree

2 files changed

+106
-43
lines changed

2 files changed

+106
-43
lines changed

CMakeUserPresets.json.example

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,34 @@
280280
"lhs": "${hostSystemName}",
281281
"rhs": "Darwin"
282282
}
283+
},
284+
{
285+
"name": "debug-macos-fast",
286+
"displayName": "Debug with Optimized Dependencies (macOS)",
287+
"description": "Preset for building MrDocs in Debug mode with the default compiler in macOS.",
288+
"inherits": "debug",
289+
"binaryDir": "${sourceDir}/build/${presetName}",
290+
"cacheVariables": {
291+
"CMAKE_BUILD_TYPE": "Debug",
292+
"LLVM_ROOT": "$env{HOME}/Developer/cpp-libs/llvm-project/install/release",
293+
"Clang_ROOT": "$env{HOME}/Developer/cpp-libs/llvm-project/install/release",
294+
"duktape_ROOT": "$env{HOME}/Developer/cpp-libs/duktape/install/release",
295+
"Duktape_ROOT": "$env{HOME}/Developer/cpp-libs/duktape/install/release",
296+
"libxml2_ROOT": "$env{HOME}/Developer/cpp-libs/libxml2/install/release",
297+
"LibXml2_ROOT": "$env{HOME}/Developer/cpp-libs/libxml2/install/release",
298+
"MRDOCS_BUILD_TESTS": true,
299+
"MRDOCS_BUILD_DOCS": false,
300+
"MRDOCS_GENERATE_REFERENCE": false,
301+
"MRDOCS_GENERATE_ANTORA_REFERENCE": false
302+
},
303+
"warnings": {
304+
"unusedCli": false
305+
},
306+
"condition": {
307+
"type": "equals",
308+
"lhs": "${hostSystemName}",
309+
"rhs": "Darwin"
310+
}
283311
}
284312
]
285313
}

bootstrap.py

Lines changed: 78 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class InstallOptions:
6969
mrdocs_build_tests: bool = True
7070
mrdocs_system_install: bool = field(default_factory=lambda: not running_from_mrdocs_source_dir())
7171
mrdocs_install_dir: str = field(
72-
default_factory=lambda: "<mrdocs-src-dir>/install/<mrdocs-build-type:lower>-<os:lower>-<cc:basename>" if running_from_mrdocs_source_dir() else "")
72+
default_factory=lambda: "<mrdocs-src-dir>/install/<mrdocs-build-type:lower>-<os:lower><\"-\":if(cc)><cc:basename>" if running_from_mrdocs_source_dir() else "")
7373
mrdocs_run_tests: bool = True
7474

7575
# Third-party dependencies
@@ -82,15 +82,6 @@ class InstallOptions:
8282
duktape_build_dir: str = "<duktape-src-dir>/build/<duktape-build-type:lower><\"-\":if(cc)><cc:basename>"
8383
duktape_install_dir: str = "<duktape-src-dir>/install/<duktape-build-type:lower><\"-\":if(cc)><cc:basename>"
8484

85-
# Libxml2
86-
libxml2_src_dir: str = "<third-party-src-dir>/libxml2"
87-
# purposefully does not depend on mrdocs-build-type because we only need the executable
88-
libxml2_build_type: str = "Release"
89-
libxml2_build_dir: str = "<libxml2-src-dir>/build/<libxml2-build-type:lower><\"-\":if(cc)><cc:basename>"
90-
libxml2_install_dir: str = "<libxml2-src-dir>/install/<libxml2-build-type:lower><\"-\":if(cc)><cc:basename>"
91-
libxml2_repo: str = "https://github.com/GNOME/libxml2"
92-
libxml2_branch: str = "v2.12.6"
93-
9485
# LLVM
9586
llvm_src_dir: str = "<third-party-src-dir>/llvm-project"
9687
llvm_build_type: str = "<mrdocs-build-type>"
@@ -99,6 +90,14 @@ class InstallOptions:
9990
llvm_repo: str = "https://github.com/llvm/llvm-project.git"
10091
llvm_commit: str = "dd7a3d4d798e30dfe53b5bbbbcd9a23c24ea1af9"
10192

93+
# Libxml2
94+
libxml2_src_dir: str = "<third-party-src-dir>/libxml2"
95+
libxml2_build_type: str = "Release" # purposefully does not depend on mrdocs-build-type because we only need the executable
96+
libxml2_build_dir: str = "<libxml2-src-dir>/build/<libxml2-build-type:lower><\"-\":if(cc)><cc:basename>"
97+
libxml2_install_dir: str = "<libxml2-src-dir>/install/<libxml2-build-type:lower><\"-\":if(cc)><cc:basename>"
98+
libxml2_repo: str = "https://github.com/GNOME/libxml2"
99+
libxml2_branch: str = "v2.12.6"
100+
102101
# Information to create run configurations
103102
generate_run_configs: bool = field(default_factory=lambda: running_from_mrdocs_source_dir())
104103
jetbrains_run_config_dir: str = "<mrdocs-src-dir>/.run"
@@ -107,6 +106,7 @@ class InstallOptions:
107106
# Meta
108107
non_interactive: bool = False
109108

109+
110110
# Constant for option descriptions
111111
INSTALL_OPTION_DESCRIPTIONS = {
112112
"cc": "Path to the C compiler executable. Leave empty for default.",
@@ -117,7 +117,7 @@ class InstallOptions:
117117
"mrdocs_src_dir": "MrDocs source directory.",
118118
"mrdocs_repo": "URL of the MrDocs repository to clone.",
119119
"mrdocs_branch": "Branch or tag of the MrDocs repository to use.",
120-
"mrdocs_build_type": "CMake build type for MrDocs (Release, Debug, RelWithDebInfo, MilRelSize, DebWithOpt).",
120+
"mrdocs_build_type": "CMake build type for MrDocs (Release, Debug, RelWithDebInfo, MilRelSize).",
121121
"mrdocs_use_user_presets": "Whether to use CMake User Presets for building MrDocs.",
122122
"mrdocs_preset_name": "Name of the CMake User Preset to use for MrDocs.",
123123
"mrdocs_build_dir": "Directory where MrDocs will be built.",
@@ -128,17 +128,17 @@ class InstallOptions:
128128
"third_party_src_dir": "Directory for all third-party source dependencies.",
129129
"duktape_src_dir": "Directory for the Duktape source code.",
130130
"duktape_url": "Download URL for the Duktape source archive.",
131-
"duktape_build_type": "CMake build type for Duktape. (Release, Debug, RelWithDebInfo, MilRelSize, DebWithOpt)",
131+
"duktape_build_type": "CMake build type for Duktape. (Release, Debug, RelWithDebInfo, MilRelSize)",
132132
"duktape_build_dir": "Directory where Duktape will be built.",
133133
"duktape_install_dir": "Directory where Duktape will be installed.",
134134
"libxml2_src_dir": "Directory for the libxml2 source code.",
135-
"libxml2_build_type": "CMake build type for libxml2. (Release, Debug, RelWithDebInfo, MilRelSize, DebWithOpt)",
135+
"libxml2_build_type": "CMake build type for libxml2: Release always recommended. (Release, Debug, RelWithDebInfo, MilRelSize)",
136136
"libxml2_build_dir": "Directory where libxml2 will be built.",
137137
"libxml2_install_dir": "Directory where libxml2 will be installed.",
138138
"libxml2_repo": "URL of the libxml2 repository to clone.",
139139
"libxml2_branch": "Branch or tag of libxml2 to use.",
140140
"llvm_src_dir": "Directory for the LLVM project source code.",
141-
"llvm_build_type": "CMake build type for LLVM. (Release, Debug, RelWithDebInfo, MilRelSize, DebWithOpt)",
141+
"llvm_build_type": "CMake build type for LLVM. (Release, Debug, RelWithDebInfo, MilRelSize)",
142142
"llvm_build_dir": "Directory where LLVM will be built.",
143143
"llvm_install_dir": "Directory where LLVM will be installed.",
144144
"llvm_repo": "URL of the LLVM project repository to clone.",
@@ -177,7 +177,7 @@ def prompt_string(self, prompt, default):
177177
Prompts the user for a string input with a default value.
178178
179179
:param prompt: The prompt message to display to the user.
180-
: param default: The default value to use if the user does not provide input.
180+
:param default: The default value to use if the user does not provide input.
181181
:return:
182182
"""
183183
if self.options.non_interactive and default is not None:
@@ -190,7 +190,10 @@ def prompt_string(self, prompt, default):
190190
RESET = "\033[0m"
191191
if self.supports_ansi():
192192
prompt = f"{BLUE}{prompt}{RESET}"
193-
inp = input(f"{prompt} ({default}): ")
193+
if default:
194+
prompt += f" ({default})"
195+
prompt += f": "
196+
inp = input(prompt)
194197
result = inp.strip() or default
195198
return result
196199

@@ -333,7 +336,7 @@ def reprompt_option(self, name):
333336

334337
def prompt_build_type_option(self, name):
335338
value = self.prompt_option(name)
336-
valid_build_types = ["Debug", "Release", "RelWithDebInfo", "DebWithOpt", "MinSizeRel"]
339+
valid_build_types = ["Debug", "Release", "RelWithDebInfo", "MinSizeRel"]
337340
for t in valid_build_types:
338341
if t.lower() == value.lower():
339342
setattr(self.options, name, t)
@@ -430,28 +433,24 @@ def is_macos(self):
430433
def cmake_workflow(self, src_dir, build_type, build_dir, install_dir, extra_args=None):
431434
"""
432435
Configures and builds a CMake project.
433-
:param src: The source directory containing the CMakeLists.txt file.
434-
:param build: The build directory where the project will be built.
435-
:param extra_args: Additional arguments to pass to the CMake configuration command.
436-
:return: None
437436
"""
438437
config_args = [self.options.cmake_path, "-S", src_dir]
439438

440439
if self.options.cc and self.options.cxx:
441440
config_args.extend(["-DCMAKE_C_COMPILER=" + self.options.cc,
442441
"-DCMAKE_CXX_COMPILER=" + self.options.cxx])
443442

444-
# "DebWithOpt" is not a valid type. However, we interpret it as a special case
443+
# "OptimizedDebug" is not a valid build type. We interpret it as a special case
445444
# where the build type is Debug and optimizations are enabled.
446-
# This is not very different from RelWithDebInfo on Unix, but ensures
447-
# Debug flags are used on Windows.
448-
build_type_is_debwithopt = build_type.lower() == 'debwithopt'
449-
cmake_build_type = build_type if not build_type_is_debwithopt else "Debug"
445+
# This is equivalent to RelWithDebInfo on Unix, but ensures
446+
# Debug flags and the Debug ABI are used on Windows.
447+
build_type_is_optimizeddebug = build_type.lower() == 'optimizeddebug'
448+
cmake_build_type = build_type if not build_type_is_optimizeddebug else "Debug"
450449
if build_dir:
451450
config_args.extend(["-B", build_dir])
452451
if build_type:
453452
config_args.extend([f"-DCMAKE_BUILD_TYPE={cmake_build_type}"])
454-
if build_type_is_debwithopt:
453+
if build_type_is_optimizeddebug:
455454
if self.is_windows():
456455
config_args.extend(["-DCMAKE_CXX_FLAGS=/DWIN32 /D_WINDOWS /Ob1 /O2 /Zi",
457456
"-DCMAKE_C_FLAGS=/DWIN32 /D_WINDOWS /Ob1 /O2 /Zi"])
@@ -514,7 +513,8 @@ def check_compilers(self):
514513
if not os.path.isabs(getattr(self.options, option)):
515514
exec = shutil.which(getattr(self.options, option))
516515
if exec is None:
517-
raise FileNotFoundError(f"{option} executable '{getattr(self.options, option)}' not found in PATH.")
516+
raise FileNotFoundError(
517+
f"{option} executable '{getattr(self.options, option)}' not found in PATH.")
518518
setattr(self.options, option, exec)
519519
if not self.is_executable(getattr(self.options, option)):
520520
raise FileNotFoundError(f"{option} executable not found at {getattr(self.options, option)}.")
@@ -524,7 +524,7 @@ def check_tools(self):
524524
for tool in tools:
525525
self.check_tool(tool)
526526

527-
def setup_mrdocs_dir(self):
527+
def setup_mrdocs_src_dir(self):
528528
self.prompt_option("mrdocs_src_dir")
529529
if not os.path.isabs(self.options.mrdocs_src_dir):
530530
self.options.mrdocs_src_dir = os.path.abspath(self.options.mrdocs_src_dir)
@@ -582,6 +582,14 @@ def setup_third_party_dir(self):
582582
self.prompt_dependency_path_option("third_party_src_dir")
583583
os.makedirs(self.options.third_party_src_dir, exist_ok=True)
584584

585+
def is_abi_compatible(self, build_type_a, build_type_b):
586+
if not self.is_windows():
587+
return True
588+
# On Windows, Debug and Release builds are not ABI compatible
589+
build_type_a_is_debug = build_type_a.lower() == "debug"
590+
build_type_b_is_debug = build_type_b.lower() == "debug"
591+
return build_type_a_is_debug == build_type_b_is_debug
592+
585593
def install_duktape(self):
586594
self.prompt_dependency_path_option("duktape_src_dir")
587595
if not os.path.exists(self.options.duktape_src_dir):
@@ -614,6 +622,16 @@ def install_duktape(self):
614622
else:
615623
print(f"Warning: {duk_config_path} does not exist. Skipping patch.")
616624
self.prompt_build_type_option("duktape_build_type")
625+
if not self.is_abi_compatible(self.options.mrdocs_build_type, self.options.duktape_build_type):
626+
if self.options.mrdocs_build_type.lower() == "debug":
627+
# User asked for Release dependency, so we do the best we can and change it to
628+
# an optimized debug build.
629+
self.options.duktape_build_type = "OptimizedDebug"
630+
else:
631+
# User asked for a Debug dependency with Release build type for MrDocs.
632+
# The dependency should just copy the release type here. Other options wouldn't make sense
633+
# because we can't even debug it.
634+
self.options.duktape_build_type = self.options.mrdocs_build_type
617635
self.prompt_dependency_path_option("duktape_build_dir")
618636
self.prompt_dependency_path_option("duktape_install_dir")
619637
self.cmake_workflow(self.options.duktape_src_dir, self.options.duktape_build_type,
@@ -686,6 +704,12 @@ def install_llvm(self):
686704
patch_path = os.path.join(llvm_patches, patch_file)
687705
shutil.copy(patch_path, llvm_subproject_dir)
688706
self.prompt_build_type_option("llvm_build_type")
707+
# Same logic as for Duktape
708+
if not self.is_abi_compatible(self.options.mrdocs_build_type, self.options.llvm_build_type):
709+
if self.options.mrdocs_build_type.lower() == "debug":
710+
self.options.llvm_build_type = "OptimizedDebug"
711+
else:
712+
self.options.llvm_build_type = self.options.mrdocs_build_type
689713
self.prompt_dependency_path_option("llvm_build_dir")
690714
self.prompt_dependency_path_option("llvm_install_dir")
691715
cmake_preset = f"{self.options.llvm_build_type.lower()}-win" if self.is_windows() else f"{self.options.llvm_build_type.lower()}-unix"
@@ -714,6 +738,9 @@ def create_cmake_presets(self):
714738
user_presets = json.load(f)
715739

716740
# Come up with a nice user preset name
741+
is_debug_fast = self.options.mrdocs_build_type.lower() == "debug" and self.options.llvm_build_type != self.options.mrdocs_build_type
742+
if is_debug_fast:
743+
self.default_options.mrdocs_preset_name += "-fast"
717744
self.prompt_option("mrdocs_preset_name")
718745

719746
# Upsert the preset in the "configurePresets" array of objects
@@ -728,24 +755,30 @@ def create_cmake_presets(self):
728755
OSDisplayName = hostSystemName
729756
if OSDisplayName == "Darwin":
730757
OSDisplayName = "macOS"
758+
759+
# Preset inherits from the parent preset based on the build type
731760
parent_preset_name = "debug"
732-
if self.options.mrdocs_build_type.lower() == "release":
761+
if self.options.mrdocs_build_type.lower() != "debug":
733762
parent_preset_name = "release"
734-
elif self.options.mrdocs_build_type.lower() == "relwithdebinfo":
735-
parent_preset_name = "relwithdebinfo"
736-
build_type_is_debwithopt = self.options.mrdocs_build_type.lower() == 'debwithopt'
737-
cmake_build_type = self.options.mrdocs_build_type if not build_type_is_debwithopt else "Debug"
738-
display_name = f"{self.options.mrdocs_build_type} {OSDisplayName}"
763+
if self.options.mrdocs_build_type.lower() == "relwithdebinfo":
764+
parent_preset_name = "relwithdebinfo"
765+
766+
# Nice display name for the preset
767+
display_name = f"{self.options.mrdocs_build_type}"
768+
if self.options.mrdocs_build_type.lower() == "debug" and self.options.llvm_build_type != self.options.mrdocs_build_type:
769+
display_name += " with Optimized Dependencies"
770+
display_name += f" ({OSDisplayName})"
739771
if self.options.cc:
740772
display_name += f" ({os.path.basename(self.options.cc)})"
773+
741774
new_preset = {
742775
"name": self.options.mrdocs_preset_name,
743776
"displayName": display_name,
744777
"description": f"Preset for building MrDocs in {self.options.mrdocs_build_type} mode with the {os.path.basename(self.options.cc) if self.options.cc else 'default'} compiler in {OSDisplayName}.",
745778
"inherits": parent_preset_name,
746779
"binaryDir": "${sourceDir}/build/${presetName}",
747780
"cacheVariables": {
748-
"CMAKE_BUILD_TYPE": cmake_build_type,
781+
"CMAKE_BUILD_TYPE": self.options.mrdocs_build_type,
749782
"LLVM_ROOT": self.options.llvm_install_dir,
750783
"Clang_ROOT": self.options.llvm_install_dir,
751784
"duktape_ROOT": self.options.duktape_install_dir,
@@ -808,16 +841,16 @@ def create_cmake_presets(self):
808841

809842
def install_mrdocs(self):
810843
if not self.options.mrdocs_use_user_presets:
811-
# Build directory specified in the preset
812844
self.prompt_option("mrdocs_build_dir")
813845
else:
814846
self.options.mrdocs_build_dir = os.path.join(self.options.mrdocs_src_dir, "build",
815847
self.options.mrdocs_preset_name)
816848
self.default_options.mrdocs_build_dir = self.options.mrdocs_build_dir
817849

818850
if not self.prompt_option("mrdocs_system_install"):
819-
# Build directory specified in the preset
820-
# self.prompt_option("mrdocs_build_dir")
851+
if self.options.mrdocs_use_user_presets:
852+
self.default_options.mrdocs_install_dir = os.path.join(self.options.mrdocs_src_dir, "install",
853+
self.options.mrdocs_preset_name)
821854
self.prompt_option("mrdocs_install_dir")
822855

823856
extra_args = []
@@ -947,7 +980,8 @@ def generate_clion_run_configs(self, configs):
947980
if 'folder' in config:
948981
attrib["folderName"] = config["folder"]
949982
clion_config = ET.SubElement(root, "configuration", attrib)
950-
ET.SubElement(clion_config, "option", name="SCRIPT_TEXT", value=f"bash {shlex.quote(config['script'])}")
983+
ET.SubElement(clion_config, "option", name="SCRIPT_TEXT",
984+
value=f"bash {shlex.quote(config['script'])}")
951985
ET.SubElement(clion_config, "option", name="INDEPENDENT_SCRIPT_PATH", value="true")
952986
ET.SubElement(clion_config, "option", name="SCRIPT_PATH", value=config["script"])
953987
ET.SubElement(clion_config, "option", name="SCRIPT_OPTIONS", value="")
@@ -1005,7 +1039,8 @@ def generate_clion_run_configs(self, configs):
10051039
if 'folder' in config:
10061040
attrib["folderName"] = config["folder"]
10071041
clion_config = ET.SubElement(root, "configuration", attrib)
1008-
ET.SubElement(clion_config, "option", name="SCRIPT_TEXT", value=f"{shlex.quote(config['script'])} {' '.join(shlex.quote(arg) for arg in config['args'])}")
1042+
ET.SubElement(clion_config, "option", name="SCRIPT_TEXT",
1043+
value=f"{shlex.quote(config['script'])} {' '.join(shlex.quote(arg) for arg in config['args'])}")
10091044
ET.SubElement(clion_config, "option", name="INDEPENDENT_SCRIPT_PATH", value="true")
10101045
ET.SubElement(clion_config, "option", name="SCRIPT_PATH", value=config["script"])
10111046
ET.SubElement(clion_config, "option", name="SCRIPT_OPTIONS", value="")
@@ -1345,12 +1380,12 @@ def generate_run_configs(self):
13451380
def install_all(self):
13461381
self.check_compilers()
13471382
self.check_tools()
1348-
self.setup_mrdocs_dir()
1383+
self.setup_mrdocs_src_dir()
13491384
self.setup_third_party_dir()
13501385
self.install_duktape()
1386+
self.install_llvm()
13511387
if self.prompt_option("mrdocs_build_tests"):
13521388
self.install_libxml2()
1353-
self.install_llvm()
13541389
self.create_cmake_presets()
13551390
self.install_mrdocs()
13561391
if self.prompt_option("generate_run_configs"):

0 commit comments

Comments
 (0)