diff --git a/.gitignore b/.gitignore index 7e798286b..be9e97d76 100644 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,5 @@ venv/ *.kdev4 imgui.ini .DS_Store -./CMakeUserPresets.json +CMakeUserPresets.json Brewfile.lock.json diff --git a/CMakeLists.txt b/CMakeLists.txt index acb545df0..1197251dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,23 +22,29 @@ option(IMHEX_ENABLE_STD_ASSERTS "Enable debug asserts in the C++ std lib option(IMHEX_ENABLE_UNIT_TESTS "Enable building unit tests" OFF) option(IMHEX_ENABLE_PRECOMPILED_HEADERS "Enable precompiled headers" OFF) +set(IMHEX_BASE_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}") +set(CMAKE_MODULE_PATH "${IMHEX_BASE_FOLDER}/cmake/modules") + +# Optional IDE support +include("${IMHEX_BASE_FOLDER}/cmake/ide_helpers.cmake") + # Basic compiler and cmake configurations set(CMAKE_CXX_STANDARD 23) set(CMAKE_INCLUDE_DIRECTORIES_BEFORE ON) -set(IMHEX_BASE_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}") -set(CMAKE_MODULE_PATH "${IMHEX_BASE_FOLDER}/cmake/modules") include("${IMHEX_BASE_FOLDER}/cmake/build_helpers.cmake") # Setup project loadVersion(IMHEX_VERSION) setVariableInParent(IMHEX_VERSION ${IMHEX_VERSION}) configureCMake() + project(imhex LANGUAGES C CXX VERSION ${IMHEX_VERSION} DESCRIPTION "The ImHex Hex Editor" HOMEPAGE_URL "https://imhex.werwolv.net" ) +configureProject() # Add ImHex sources add_custom_target(imhex_all ALL) @@ -75,3 +81,6 @@ generateSDKDirectory() # Handle package generation createPackage() + +# Accomodate IDEs with FOLDER support +tweakTargetsForIDESupport() diff --git a/CMakePresets.json b/CMakePresets.json index 9b45394d9..83c9e5342 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -28,6 +28,23 @@ "displayName": "x86_64 Build", "description": "x86_64 build", "inherits": [ "base" ] + }, + { + "name": "xcode", + "inherits": [ "base" ], + + "displayName": "Xcode", + "description": "Xcode with external compiler override", + "generator": "Xcode", + + "cacheVariables": { + "CMAKE_C_COMPILER": "clang", + + "CMAKE_CXX_COMPILER": "clang++", + "CMAKE_CXX_FLAGS": "-fexperimental-library -Wno-shorten-64-to-32 -Wno-deprecated-declarations", + + "IMHEX_IDE_HELPERS_OVERRIDE_XCODE_COMPILER": "ON" + } } ], "buildPresets": [ diff --git a/cmake/build_helpers.cmake b/cmake/build_helpers.cmake index dd1dc0a5a..b19d69261 100644 --- a/cmake/build_helpers.cmake +++ b/cmake/build_helpers.cmake @@ -152,14 +152,12 @@ macro(addPluginDirectories) foreach (plugin IN LISTS PLUGINS) add_subdirectory("plugins/${plugin}") if (TARGET ${plugin}) - set_target_properties(${plugin} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins) - set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins) + set_target_properties(${plugin} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${IMHEX_MAIN_OUTPUT_DIRECTORY}/plugins") + set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${IMHEX_MAIN_OUTPUT_DIRECTORY}/plugins") if (APPLE) if (IMHEX_GENERATE_PACKAGE) set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PLUGINS_INSTALL_LOCATION}) - else () - set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins) endif () else () if (WIN32) @@ -275,6 +273,8 @@ macro(createPackage) endif() install(CODE [[ message(STATUS "MacOS Bundle finalized. DO NOT TOUCH IT ANYMORE! ANY MODIFICATIONS WILL BREAK IT FROM NOW ON!") ]]) + else() + downloadImHexPatternsFiles("${IMHEX_MAIN_OUTPUT_DIRECTORY}") endif() else() install(TARGETS main RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) @@ -342,8 +342,11 @@ macro(configureCMake) if (LD_LLD_PATH) set(CMAKE_LINKER ${LD_LLD_PATH}) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fuse-ld=lld") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=lld") + + if (NOT XCODE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fuse-ld=lld") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=lld") + endif() else () message(WARNING "lld not found, using default linker!") endif () @@ -378,6 +381,15 @@ macro(configureCMake) set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "Disable deprecated warnings" FORCE) endmacro() +function(configureProject) + if (XCODE) + # Support Xcode's multi configuration paradigm by placing built artifacts into separate directories + set(IMHEX_MAIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/Configs/$" PARENT_SCOPE) + else() + set(IMHEX_MAIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" PARENT_SCOPE) + endif() +endfunction() + macro(setDefaultBuiltTypeIfUnset) if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Using RelWithDebInfo build type as it was left unset" FORCE) @@ -480,14 +492,40 @@ function(downloadImHexPatternsFiles dest) message(STATUS "Finished downloading ImHex-Patterns") else () + set(imhex_patterns_SOURCE_DIR "") + # Maybe patterns are cloned to a subdirectory - set(imhex_patterns_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ImHex-Patterns") + if (NOT EXISTS ${imhex_patterns_SOURCE_DIR}) + set(imhex_patterns_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ImHex-Patterns") + endif() + + # Or a sibling directory + if (NOT EXISTS ${imhex_patterns_SOURCE_DIR}) + set(imhex_patterns_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../ImHex-Patterns") + endif() endif () - if (EXISTS ${imhex_patterns_SOURCE_DIR}) + if (NOT EXISTS ${imhex_patterns_SOURCE_DIR}) + message(WARNING "Failed to locate ImHex-Patterns repository, some resources will be missing during install!") + elseif(XCODE) + # The Xcode build has multiple configurations, which each need a copy of these files + file(GLOB_RECURSE sourceFilePaths LIST_DIRECTORIES NO CONFIGURE_DEPENDS RELATIVE "${imhex_patterns_SOURCE_DIR}" + "${imhex_patterns_SOURCE_DIR}/constants/*" + "${imhex_patterns_SOURCE_DIR}/encodings/*" + "${imhex_patterns_SOURCE_DIR}/includes/*" + "${imhex_patterns_SOURCE_DIR}/patterns/*" + "${imhex_patterns_SOURCE_DIR}/magic/*" + "${imhex_patterns_SOURCE_DIR}/nodes/*" + ) + list(FILTER sourceFilePaths EXCLUDE REGEX "_schema.json$") + + foreach(relativePath IN LISTS sourceFilePaths) + file(GENERATE OUTPUT "${dest}/${relativePath}" INPUT "${imhex_patterns_SOURCE_DIR}/${relativePath}") + endforeach() + else() set(PATTERNS_FOLDERS_TO_INSTALL constants encodings includes patterns magic nodes) foreach (FOLDER ${PATTERNS_FOLDERS_TO_INSTALL}) - install(DIRECTORY "${imhex_patterns_SOURCE_DIR}/${FOLDER}" DESTINATION ${dest} PATTERN "**/_schema.json" EXCLUDE) + install(DIRECTORY "${imhex_patterns_SOURCE_DIR}/${FOLDER}" DESTINATION "${dest}" PATTERN "**/_schema.json" EXCLUDE) endforeach () endif () diff --git a/cmake/ide_helpers.cmake b/cmake/ide_helpers.cmake new file mode 100644 index 000000000..f908dddfe --- /dev/null +++ b/cmake/ide_helpers.cmake @@ -0,0 +1,149 @@ + +option(IMHEX_IDE_HELPERS_OVERRIDE_XCODE_COMPILER "Enable choice of compiler for Xcode builds, despite CMake's best efforts" OFF) +option(IMHEX_IDE_HELPERS_INTRUSIVE_IDE_TWEAKS "Enable intrusive CMake tweaks to better support IDEs with folder support" OFF) + +# The CMake infrastructure silently ignores the CMAKE_<>_COMPILER settings when +# using the `Xcode` generator. +# +# A particularly nasty (and potentially only) way of getting around this is to +# temporarily lie about the generator being used, while CMake determines and +# locks in the compiler to use. +# +# Needless to say, this is hacky and fragile. Use at your own risk! +if (IMHEX_IDE_HELPERS_OVERRIDE_XCODE_COMPILER AND CMAKE_GENERATOR STREQUAL "Xcode") + set(CMAKE_GENERATOR "Unknown") + enable_language(C CXX) + + set(CMAKE_GENERATOR "Xcode") + + set(CMAKE_XCODE_ATTRIBUTE_CC "${CMAKE_C_COMPILER}") + set(CMAKE_XCODE_ATTRIBUTE_CXX "${CMAKE_CXX_COMPILER}") + + if (CLANG) + set(CMAKE_XCODE_ATTRIBUTE_LD "${CMAKE_C_COMPILER}") + set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS "${CMAKE_CXX_COMPILER}") + endif() + + # By default Xcode passes a `-index-store-path=<...>` parameter to the compiler + # during builds to build code completion indexes. This is not supported by + # anything other than AppleClang + set(CMAKE_XCODE_ATTRIBUTE_COMPILER_INDEX_STORE_ENABLE "NO") +endif() + +# Generate a launch/build scheme for all targets +set(CMAKE_XCODE_GENERATE_SCHEME YES) + +# Utility function that helps avoid messing with non-standard targets +macro(returnIfTargetIsNonTweakable target) + get_target_property(targetIsAliased ${target} ALIASED_TARGET) + get_target_property(targetIsImported ${target} IMPORTED) + + if (targetIsAliased OR targetIsImported) + return() + endif() + + get_target_property(targetType ${target} TYPE) + if (targetType MATCHES "INTERFACE_LIBRARY|UNKNOWN_LIBRARY") + return() + endif() +endmacro() + +# Targets usually don't specify their private headers, nor group their source files +# which results in very spotty coverage by IDEs with folders support +# +# Unfortunately, CMake does not have a `target_source_group` like construct yet, therefore +# we have to play by the limitations of `source_group`. +# +# A particularly problematic part is that the function must be called within the directoryies +# scope for the grouping to take effect. +# +# See: https://discourse.cmake.org/t/topic/7388 +function(tweakTargetForIDESupport target) + returnIfTargetIsNonTweakable(${target}) + + # Don't assume directory structure of third parties + get_target_property(targetSourceDir ${target} SOURCE_DIR) + if (targetSourceDir MATCHES "third_party") + return() + endif() + + # Add headers to target + get_target_property(targetSourceDir ${target} SOURCE_DIR) + if (targetSourceDir) + file(GLOB_RECURSE targetPrivateHeaders CONFIGURE_DEPENDS "${targetSourceDir}/include/*.hpp") + + target_sources(${target} PRIVATE "${targetPrivateHeaders}") + endif() + + # Organize target sources into directory tree + get_target_property(sources ${target} SOURCES) + foreach(file IN LISTS sources) + get_filename_component(path "${file}" ABSOLUTE) + + if (NOT path MATCHES "^${targetSourceDir}") + continue() + endif() + + source_group(TREE "${targetSourceDir}" PREFIX "Source Tree" FILES "${file}") + endforeach() +endfunction() + +if (IMHEX_IDE_HELPERS_INTRUSIVE_IDE_TWEAKS) + # See tweakTargetForIDESupport for rationale + + function(add_library target) + _add_library(${target} ${ARGN}) + + tweakTargetForIDESupport(${target}) + endfunction() + + function(add_executable target) + _add_executable(${target} ${ARGN}) + + tweakTargetForIDESupport(${target}) + endfunction() +endif() + +# Adjust target's FOLDER property, which is an IDE only preference +function(_tweakTarget target path) + get_target_property(targetType ${target} TYPE) + + if (TARGET generator-${target}) + set_target_properties(generator-${target} PROPERTIES FOLDER "romfs/${target}") + endif() + if (TARGET romfs_file_packer-${target}) + set_target_properties(romfs_file_packer-${target} PROPERTIES FOLDER "romfs/${target}") + endif() + if (TARGET libromfs-${target}) + set_target_properties(libromfs-${target} PROPERTIES FOLDER "romfs/${target}") + endif() + + if (${targetType} MATCHES "EXECUTABLE|LIBRARY") + set_target_properties(${target} PROPERTIES FOLDER "${path}") + endif() +endfunction() + +macro(_tweakTargetsRecursive dir) + get_property(subdirectories DIRECTORY ${dir} PROPERTY SUBDIRECTORIES) + foreach(subdir IN LISTS subdirectories) + _tweakTargetsRecursive("${subdir}") + endforeach() + + if(${dir} STREQUAL ${CMAKE_SOURCE_DIR}) + return() + endif() + + get_property(targets DIRECTORY "${dir}" PROPERTY BUILDSYSTEM_TARGETS) + file(RELATIVE_PATH rdir ${CMAKE_SOURCE_DIR} "${dir}/..") + + foreach(target ${targets}) + _tweakTarget(${target} "${rdir}") + endforeach() +endmacro() + +# Tweak all targets this CMake build is aware about +function(tweakTargetsForIDESupport) + set_property(GLOBAL PROPERTY USE_FOLDERS ON) + + _tweakTargetsRecursive("${CMAKE_SOURCE_DIR}") +endfunction() diff --git a/cmake/modules/ImHexPlugin.cmake b/cmake/modules/ImHexPlugin.cmake index 62b688cde..f176c6e70 100644 --- a/cmake/modules/ImHexPlugin.cmake +++ b/cmake/modules/ImHexPlugin.cmake @@ -55,7 +55,7 @@ macro(add_imhex_plugin) # Configure build properties set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins + RUNTIME_OUTPUT_DIRECTORY "${IMHEX_MAIN_OUTPUT_DIRECTORY}/plugins" CXX_STANDARD 23 PREFIX "" SUFFIX ${IMHEX_PLUGIN_SUFFIX} diff --git a/main/gui/CMakeLists.txt b/main/gui/CMakeLists.txt index 35c0876e3..65a6d26b6 100644 --- a/main/gui/CMakeLists.txt +++ b/main/gui/CMakeLists.txt @@ -51,8 +51,9 @@ if (EMSCRIPTEN) endif () set_target_properties(main PROPERTIES - OUTPUT_NAME ${IMHEX_APPLICATION_NAME} - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../..) + OUTPUT_NAME "${IMHEX_APPLICATION_NAME}" + RUNTIME_OUTPUT_DIRECTORY "${IMHEX_MAIN_OUTPUT_DIRECTORY}" +) target_compile_definitions(main PRIVATE IMHEX_PROJECT_NAME="${PROJECT_NAME}") @@ -67,4 +68,4 @@ precompileHeaders(main ${CMAKE_CURRENT_SOURCE_DIR}/include) if (APPLE) add_compile_definitions(GL_SILENCE_DEPRECATION) -endif () \ No newline at end of file +endif ()