├── .gitignore ├── .dockerignore ├── llvm.cmake ├── ubuntu-dependencies.sh ├── XEDConfig.cmake.in ├── Dockerfile ├── xed.cmake ├── kitware-archive.sh ├── hash.py ├── CMakeLists.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | build*/ 2 | cmake-build*/ 3 | .idea/ 4 | .vscode/ -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | build*/ 2 | cmake-build*/ 3 | .idea/ 4 | .vscode/ 5 | Dockerfile 6 | *.md 7 | .dockerignore 8 | .git/ -------------------------------------------------------------------------------- /llvm.cmake: -------------------------------------------------------------------------------- 1 | ExternalProject_Add(llvm 2 | URL 3 | "https://github.com/llvm/llvm-project/releases/download/llvmorg-17.0.6/llvm-project-17.0.6.src.tar.xz" 4 | URL_HASH 5 | "SHA256=58a8818c60e6627064f312dbf46c02d9949956558340938b71cf731ad8bc0813" 6 | CMAKE_CACHE_ARGS 7 | ${CMAKE_ARGS} 8 | "-DLLVM_ENABLE_PROJECTS:STRING=clang;lld" 9 | "-DLLVM_ENABLE_ASSERTIONS:STRING=ON" 10 | "-DLLVM_ENABLE_DUMP:STRING=ON" 11 | "-DLLVM_ENABLE_RTTI:STRING=ON" 12 | "-DLLVM_ENABLE_LIBEDIT:STRING=OFF" 13 | "-DLLVM_PARALLEL_LINK_JOBS:STRING=1" 14 | "-DLLVM_ENABLE_DIA_SDK:STRING=OFF" 15 | CMAKE_GENERATOR 16 | "Ninja" 17 | SOURCE_SUBDIR 18 | "llvm" 19 | ) -------------------------------------------------------------------------------- /ubuntu-dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Determine the right libstdc++ cross-compilation package 4 | # Reference: https://packages.ubuntu.com/jammy/devel/ 5 | arch=$(uname -m) 6 | if [ "$arch" == "aarch64" ]; then 7 | cross_packages="libstdc++-12-dev-armhf-cross" 8 | elif [ "$arch" == "x86_64" ]; then 9 | cross_packages="libstdc++-9-dev-i386-cross" 10 | else 11 | echo "Unsupported architecture: $arch" 12 | exit 1 13 | fi 14 | 15 | # Install the dependencies 16 | ./kitware-archive.sh 17 | apt-get update && apt-get install -y \ 18 | git \ 19 | cmake \ 20 | ninja-build \ 21 | flex \ 22 | bison \ 23 | clang \ 24 | libxml2-dev \ 25 | ncurses-dev \ 26 | libz-dev \ 27 | libsqlite3-dev \ 28 | sqlite3 \ 29 | $cross_packages \ 30 | && rm -rf /var/lib/apt/lists/* 31 | -------------------------------------------------------------------------------- /XEDConfig.cmake.in: -------------------------------------------------------------------------------- 1 | # Reference: https://github.com/lifting-bits/cxx-common/blob/master/ports/xed/XEDConfig.cmake 2 | 3 | if(XED_FOUND) 4 | return() 5 | endif() 6 | 7 | get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE) 8 | 9 | find_library(XED_LIBRARY xed PATHS "${PACKAGE_PREFIX_DIR}/lib" NO_CACHE REQUIRED NO_DEFAULT_PATH) 10 | add_library(XED::XED STATIC IMPORTED) 11 | set_target_properties(XED::XED PROPERTIES 12 | IMPORTED_CONFIGURATIONS "NOCONFIG" 13 | IMPORTED_LOCATION_NOCONFIG "${XED_LIBRARY}" 14 | INTERFACE_INCLUDE_DIRECTORIES "${PACKAGE_PREFIX_DIR}/include" 15 | ) 16 | 17 | find_library(ILD_LIBRARY xed-ild PATHS "${PACKAGE_PREFIX_DIR}/lib" NO_CACHE REQUIRED NO_DEFAULT_PATH) 18 | add_library(XED::ILD STATIC IMPORTED) 19 | set_target_properties(XED::ILD PROPERTIES 20 | IMPORTED_CONFIGURATIONS "NOCONFIG" 21 | IMPORTED_LOCATION_NOCONFIG "${XED_LIBRARY}" 22 | INTERFACE_INCLUDE_DIRECTORIES "${PACKAGE_PREFIX_DIR}/include" 23 | ) 24 | 25 | set(XED_FOUND ON) 26 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Reference: https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/ 2 | 3 | # Build stage (no need to optimize for size) 4 | FROM ubuntu:22.04 AS build 5 | WORKDIR /cxx-common 6 | COPY . . 7 | RUN ./ubuntu-dependencies.sh 8 | RUN \ 9 | cmake -B build \ 10 | "-DCMAKE_C_COMPILER=$(which clang)" \ 11 | "-DCMAKE_CXX_COMPILER=$(which clang++)" \ 12 | "-DCMAKE_INSTALL_PREFIX=$(pwd)/install" \ 13 | && \ 14 | cmake --build build && \ 15 | rm -rf build 16 | 17 | # Actual final image 18 | FROM ubuntu:22.04 AS cxx-common 19 | WORKDIR /cxx-common/install 20 | COPY --from=build /cxx-common/install . 21 | ENV CMAKE_PREFIX_PATH=/cxx-common/install 22 | ENV LD_LIBRARY_PATH=/cxx-common/install/lib 23 | RUN apt-get update && apt-get install -y \ 24 | libstdc++-11-dev \ 25 | libxml2-dev \ 26 | ncurses-dev \ 27 | libz-dev \ 28 | libsqlite3-dev \ 29 | sqlite3 \ 30 | && apt remove -y gcc gcc-11 \ 31 | && apt autoremove -y \ 32 | && rm -rf /var/lib/apt/lists/* 33 | LABEL org.opencontainers.image.source=https://github.com/mrexodia/cxx-common-cmake -------------------------------------------------------------------------------- /xed.cmake: -------------------------------------------------------------------------------- 1 | find_package(Python3 COMPONENTS Interpreter REQUIRED) 2 | message(STATUS "Python3: ${Python3_EXECUTABLE}") 3 | 4 | # Reference: https://github.com/lifting-bits/cxx-common/blob/master/ports/xed/portfile.cmake 5 | 6 | # TODO: pass compiler flags 7 | 8 | set(MFILE_ARGS 9 | "install" 10 | "--install-dir=install" 11 | "--cc=${CMAKE_C_COMPILER}" 12 | "--cxx=${CMAKE_CXX_COMPILER}" 13 | ) 14 | 15 | if(CMAKE_OSX_SYSROOT) 16 | list(APPEND MFILE_ARGS "--extra-ccflags=-isysroot ${CMAKE_OSX_SYSROOT} ${ADDITIONAL_FLAGS}") 17 | list(APPEND MFILE_ARGS "--extra-cxxflags=-isysroot ${CMAKE_OSX_SYSROOT} ${ADDITIONAL_FLAGS}") 18 | elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC") 19 | list(APPEND MFILE_ARGS "--extra-ccflags=${ADDITIONAL_FLAGS}") 20 | list(APPEND MFILE_ARGS "--extra-cxxflags=${ADDITIONAL_FLAGS}") 21 | endif() 22 | 23 | if(BUILD_SHARED_LIBS) 24 | list(APPEND MFILE_ARGS "--shared") 25 | else() 26 | list(APPEND MFILE_ARGS "--static") 27 | endif() 28 | 29 | if(CMAKE_AR) 30 | list(APPEND MFILE_ARGS "--ar=${CMAKE_AR}") 31 | endif() 32 | 33 | ExternalProject_Add(mbuild 34 | GIT_REPOSITORY 35 | "https://github.com/intelxed/mbuild" 36 | GIT_TAG 37 | "v2022.07.28" 38 | GIT_PROGRESS 39 | ON 40 | GIT_SHALLOW 41 | ON 42 | CONFIGURE_COMMAND 43 | "${CMAKE_COMMAND}" -E true 44 | BUILD_COMMAND 45 | "${CMAKE_COMMAND}" -E true 46 | INSTALL_COMMAND 47 | "${CMAKE_COMMAND}" -E true 48 | PREFIX 49 | xed-prefix 50 | ) 51 | 52 | ExternalProject_Add(xed 53 | GIT_REPOSITORY 54 | "https://github.com/intelxed/xed" 55 | GIT_TAG 56 | "v2022.10.11" 57 | GIT_PROGRESS 58 | ON 59 | GIT_SHALLOW 60 | ON 61 | CMAKE_CACHE_ARGS 62 | ${CMAKE_ARGS} 63 | CONFIGURE_COMMAND 64 | "${CMAKE_COMMAND}" -E true 65 | BUILD_COMMAND 66 | "${Python3_EXECUTABLE}" "/mfile.py" ${MFILE_ARGS} 67 | INSTALL_COMMAND 68 | "${CMAKE_COMMAND}" -E copy_directory /install "${CMAKE_INSTALL_PREFIX}" 69 | PREFIX 70 | xed-prefix 71 | ) 72 | 73 | # TODO: generate XEDVersion.cmake as well file 74 | configure_file("${CMAKE_CURRENT_SOURCE_DIR}/XEDConfig.cmake.in" "${CMAKE_INSTALL_PREFIX}/lib/cmake/XED/XEDConfig.cmake" @ONLY) -------------------------------------------------------------------------------- /kitware-archive.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | help() { 6 | echo "Usage: $0 [--release ] [--rc]" > /dev/stderr 7 | } 8 | 9 | doing= 10 | rc= 11 | release= 12 | help= 13 | for opt in "$@" 14 | do 15 | case "${doing}" in 16 | release) 17 | release="${opt}" 18 | doing= 19 | ;; 20 | "") 21 | case "${opt}" in 22 | --rc) 23 | rc=1 24 | ;; 25 | --release) 26 | doing=release 27 | ;; 28 | --help) 29 | help=1 30 | ;; 31 | esac 32 | ;; 33 | esac 34 | done 35 | 36 | if [ -n "${doing}" ] 37 | then 38 | echo "--${doing} option given no argument." > /dev/stderr 39 | echo > /dev/stderr 40 | help 41 | exit 1 42 | fi 43 | 44 | if [ -n "${help}" ] 45 | then 46 | help 47 | exit 48 | fi 49 | 50 | if [ -z "${release}" ] 51 | then 52 | unset UBUNTU_CODENAME 53 | . /etc/os-release 54 | 55 | if [ -z "${UBUNTU_CODENAME+x}" ] 56 | then 57 | echo "This is not an Ubuntu system. Aborting." > /dev/stderr 58 | exit 1 59 | fi 60 | 61 | release="${UBUNTU_CODENAME}" 62 | fi 63 | 64 | case "${release}" in 65 | noble|jammy|focal) 66 | packages= 67 | keyring_packages="ca-certificates gpg wget" 68 | ;; 69 | *) 70 | echo "Only Ubuntu Noble (24.04), Jammy (22.04), and Focal (20.04) are supported. Aborting." > /dev/stderr 71 | exit 1 72 | ;; 73 | esac 74 | 75 | get_keyring= 76 | if [ ! -f /usr/share/doc/kitware-archive-keyring/copyright ] 77 | then 78 | packages="${packages} ${keyring_packages}" 79 | get_keyring=1 80 | fi 81 | 82 | # Start the real work 83 | set -x 84 | 85 | apt-get update 86 | # shellcheck disable=SC2086 87 | apt-get install -y ${packages} 88 | 89 | test -n "${get_keyring}" && (wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - > /usr/share/keyrings/kitware-archive-keyring.gpg) 90 | 91 | echo "deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ ${release} main" > /etc/apt/sources.list.d/kitware.list 92 | if [ -n "${rc}" ] 93 | then 94 | echo "deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ ${release}-rc main" >> /etc/apt/sources.list.d/kitware.list 95 | fi 96 | 97 | apt-get update 98 | test -n "${get_keyring}" && rm /usr/share/keyrings/kitware-archive-keyring.gpg 99 | apt-get install -y kitware-archive-keyring 100 | -------------------------------------------------------------------------------- /hash.py: -------------------------------------------------------------------------------- 1 | # This script calculates a cache key for all the dependencies and their versions 2 | # It recursively processes CMakeLists.txt and the included files, plus the submodules 3 | 4 | import os 5 | import sys 6 | import hashlib 7 | import subprocess 8 | import shlex 9 | import re 10 | 11 | def hash_file(path): 12 | with open(path, "rb") as f: 13 | digest = hashlib.sha1(f.read()) 14 | return digest.hexdigest() 15 | 16 | def git(command): 17 | args = ["git"] + shlex.split(command) 18 | result = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 19 | if result.returncode != 0: 20 | raise Exception(f"git {command} failed with exit code {result.returncode}") 21 | return result.stdout.decode("utf-8").rstrip() 22 | 23 | def process_includes(cmake_dir, cmake_path): 24 | status = [f"{hash_file(cmake_path)} {os.path.basename(cmake_path)}"] 25 | with open(cmake_path, "r") as f: 26 | for line in f.readlines(): 27 | line = line.strip() 28 | if line.startswith("include("): 29 | include_file = line[8:-1] 30 | include_path = os.path.join(cmake_dir, include_file) 31 | if os.path.exists(include_path): 32 | status += process_includes(cmake_dir, include_path) 33 | return status 34 | 35 | def main(): 36 | debug = len(sys.argv) > 1 37 | 38 | # Process CMakeLists.txt and includes 39 | cmake_dir = os.path.dirname(__file__) 40 | os.chdir(cmake_dir) 41 | cmake_path = os.path.join(cmake_dir, "CMakeLists.txt") 42 | cmake_status = process_includes(cmake_dir, cmake_path) 43 | 44 | # Process submodules 45 | submodule_status = [] 46 | for dir in sorted(os.listdir(cmake_dir)): 47 | if os.path.isdir(dir): 48 | try: 49 | hash = git(f"submodule status \"{os.path.abspath(dir)}\"").strip().split(" ")[0] 50 | submodule_status.append(f"{hash} {os.path.basename(dir)}") 51 | except: 52 | pass 53 | 54 | # Calculate the final hash 55 | final_status = str.join("\n", cmake_status) 56 | final_status += "\n" 57 | final_status += str.join("\n", submodule_status) 58 | file_hash = hashlib.sha1(final_status.encode("utf-8")).hexdigest() 59 | 60 | # Extract the restore_hash from the last commit message 61 | commit_message = git("log -1 --pretty=format:%s") 62 | match = re.search(r"(reuse_cache|reuse_hash|restore_hash|cache_hash)=([0-9a-f]+)", commit_message) 63 | if match: 64 | restore_hash = match.group(2) 65 | else: 66 | restore_hash = file_hash 67 | 68 | # Print the output variables 69 | output_vars = "" 70 | output_vars += f"file_hash={file_hash}\n" 71 | output_vars += f"restore_hash={restore_hash}\n" 72 | sys.stdout.write(output_vars) 73 | 74 | # Additional debug output to stderr 75 | if debug: 76 | sys.stderr.write("Debug output:\n") 77 | sys.stderr.write(final_status + "\n") 78 | sys.stderr.write(output_vars) 79 | sys.stderr.flush() 80 | 81 | if __name__ == "__main__": 82 | main() 83 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # https://alexreinking.com/blog/how-to-use-cmake-without-the-agonizing-pain-part-1.html 2 | cmake_minimum_required(VERSION 3.22) 3 | 4 | # Bail out early for multi-config generators 5 | if(GENERATOR_IS_MULTI_CONFIG) 6 | message(FATAL_ERROR "Multi-config generators are not supported. Use Make/NMake/Ninja instead") 7 | endif() 8 | 9 | if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) 10 | message(FATAL_ERROR "In-tree builds are not supported. Run CMake from a separate directory: cmake -B build") 11 | endif() 12 | 13 | # Default to a Release config 14 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING "") 15 | if(CMAKE_BUILD_TYPE STREQUAL "") 16 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE) 17 | endif() 18 | 19 | project(Dependencies) 20 | 21 | message(STATUS "Configuration: ${CMAKE_BUILD_TYPE}") 22 | 23 | # Default to build/install (setting this variable is not recommended and might cause conflicts) 24 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 25 | set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/install" CACHE PATH "Install prefix" FORCE) 26 | endif() 27 | message(STATUS "Install prefix: ${CMAKE_INSTALL_PREFIX}") 28 | 29 | # Git is necessary for submodules 30 | find_package(Git REQUIRED) 31 | message(STATUS "Git: ${GIT_EXECUTABLE}") 32 | 33 | # Ninja is necessary for building the dependencies 34 | find_program(NINJA_EXECUTABLE ninja NO_CACHE NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH NO_CMAKE_INSTALL_PREFIX NO_CMAKE_FIND_ROOT_PATH) 35 | if(NINJA_EXECUTABLE STREQUAL "NINJA_EXECUTABLE-NOTFOUND") 36 | message(FATAL_ERROR "Could not find 'ninja' in the PATH") 37 | endif() 38 | message(STATUS "Ninja: ${NINJA_EXECUTABLE}") 39 | 40 | # Documentation: https://cmake.org/cmake/help/latest/module/ExternalProject.html 41 | include(ExternalProject) 42 | 43 | # Hook for ExternalProject_Add to make sure projects build in order 44 | function(ExternalProject_Add name) 45 | # The DEPENDS argument is fully implicit 46 | cmake_parse_arguments(HOOK "" "" DEPENDS ${ARGN}) 47 | if(HOOK_DEPENDS) 48 | message(FATAL_ERROR "Explicit DEPENDS (${HOOK_DEPENDS}) not supported") 49 | endif() 50 | 51 | # Update the LAST_EXTERNAL_PROJECT property 52 | get_property(LAST_EXTERNAL_PROJECT GLOBAL PROPERTY LAST_EXTERNAL_PROJECT) 53 | set_property(GLOBAL PROPERTY LAST_EXTERNAL_PROJECT ${name}) 54 | 55 | # Pass the previous project as a dependency to this call 56 | if(LAST_EXTERNAL_PROJECT) 57 | set(HOOK_ARGS DEPENDS "${LAST_EXTERNAL_PROJECT}") 58 | message(STATUS "ExternalProject: ${name} depends on ${LAST_EXTERNAL_PROJECT}") 59 | else() 60 | message(STATUS "ExternalProject: ${name}") 61 | endif() 62 | _ExternalProject_Add(${name} ${ARGN} ${HOOK_ARGS} 63 | # Reference: https://www.scivision.dev/cmake-external-project-ninja-verbose/ 64 | USES_TERMINAL_DOWNLOAD ON 65 | USES_TERMINAL_UPDATE ON 66 | USES_TERMINAL_PATCH ON 67 | USES_TERMINAL_CONFIGURE ON 68 | USES_TERMINAL_BUILD ON 69 | USES_TERMINAL_INSTALL ON 70 | USES_TERMINAL_TEST ON 71 | ) 72 | endfunction() 73 | 74 | if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC") 75 | # Suppress warnings for clang-cl builds, some of these cause compilation errors. 76 | set(ADDITIONAL_FLAGS 77 | "-w" 78 | ) 79 | endif() 80 | 81 | # Convert a CMake list to a space-separated list 82 | list(JOIN ADDITIONAL_FLAGS " " ADDITIONAL_FLAGS) 83 | 84 | # Default cache variables for all projects 85 | list(APPEND CMAKE_ARGS 86 | "-DCMAKE_PREFIX_PATH:FILEPATH=${CMAKE_INSTALL_PREFIX}" 87 | "-DCMAKE_INSTALL_PREFIX:FILEPATH=${CMAKE_INSTALL_PREFIX}" 88 | "-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}" 89 | "-DGFLAGS_USE_TARGET_NAMESPACE:STRING=ON" 90 | "-DBUILD_SHARED_LIBS:STRING=OFF" 91 | "-DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER}" 92 | "-DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER}" 93 | "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS} ${ADDITIONAL_FLAGS}" 94 | "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS} ${ADDITIONAL_FLAGS}" 95 | ) 96 | 97 | function(simple_git repo tag) 98 | get_filename_component(name "${repo}" NAME_WE) 99 | ExternalProject_Add(${name} 100 | GIT_REPOSITORY 101 | "${repo}" 102 | GIT_TAG 103 | "${tag}" 104 | GIT_PROGRESS 105 | ON 106 | CMAKE_CACHE_ARGS 107 | ${CMAKE_ARGS} 108 | ${ARGN} 109 | CMAKE_GENERATOR 110 | "Ninja" 111 | ) 112 | endfunction() 113 | 114 | function(simple_submodule folder) 115 | set(folder_path "${CMAKE_CURRENT_SOURCE_DIR}/${folder}") 116 | if(NOT EXISTS "${folder_path}" OR NOT EXISTS "${folder_path}/CMakeLists.txt") 117 | message(STATUS "Submodule '${folder}' not initialized, running git...") 118 | execute_process( 119 | COMMAND "${GIT_EXECUTABLE}" submodule update --init 120 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 121 | COMMAND_ERROR_IS_FATAL ANY 122 | ) 123 | endif() 124 | ExternalProject_Add(${folder} 125 | SOURCE_DIR 126 | "${folder_path}" 127 | CMAKE_CACHE_ARGS 128 | ${CMAKE_ARGS} 129 | ${ARGN} 130 | CMAKE_GENERATOR 131 | "Ninja" 132 | # Always trigger the build step (necessary because there is no download step) 133 | BUILD_ALWAYS 134 | ON 135 | ) 136 | endfunction() 137 | 138 | simple_git(https://github.com/gflags/gflags v2.2.2) 139 | simple_git(https://github.com/google/glog v0.6.0) 140 | simple_git(https://github.com/google/googletest v1.15.2 141 | "-Dgtest_force_shared_crt:STRING=ON" 142 | ) 143 | simple_git(https://github.com/Tessil/ordered-map v1.1.0) 144 | simple_git(https://github.com/fmtlib/fmt 11.0.2 145 | "-DFMT_DOC:STRING=OFF" 146 | "-DFMT_TEST:STRING=OFF" 147 | "-DFMT_SYSTEM_HEADERS:STRING=ON" 148 | ) 149 | simple_git(https://github.com/gabime/spdlog v1.14.1 150 | "-DSPDLOG_FMT_EXTERNAL:STRING=ON" 151 | "-DSPDLOG_SYSTEM_INCLUDES:STRING=ON" 152 | "-DSPDLOG_BUILD_EXAMPLE:STRING=OFF" 153 | ) 154 | simple_git(https://github.com/nlohmann/json v3.11.3 155 | "-DJSON_BuildTests:STRING=OFF" 156 | ) 157 | simple_git(https://github.com/lief-project/LIEF 0.15.1 158 | "-DLIEF_EXAMPLES:STRING=OFF" 159 | "-DLIEF_USE_CCACHE:STRING=OFF" 160 | ) 161 | simple_git(https://github.com/capstone-engine/capstone 5.0.1 162 | "-DCAPSTONE_BUILD_TESTS:STRING=OFF" 163 | ) 164 | simple_git(https://github.com/Z3Prover/z3 z3-4.13.0 165 | "-DZ3_BUILD_LIBZ3_SHARED:STRING=OFF" 166 | ) 167 | include(xed.cmake) 168 | include(llvm.cmake) 169 | simple_git(https://github.com/lifting-bits/remill 7f091d4256060c254fcd15f35fd8b9bd6abd157f # master at 2024-08-08 170 | "-DUSE_SYSTEM_DEPENDENCIES:STRING=ON" 171 | "-DREMILL_ENABLE_TESTING:STRING=OFF" 172 | ) 173 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **Migrated to: https://github.com/LLVMParty/packages** 2 | 3 | # dependencies 4 | 5 | ## Building (macOS) 6 | 7 | ```sh 8 | brew install cmake ninja flex bison 9 | cmake -B build 10 | cmake --build build 11 | ``` 12 | 13 | ## Building (Linux) 14 | 15 | **Building with GCC is not supported.** 16 | 17 | ```sh 18 | sudo ./ubuntu-dependencies.sh 19 | cmake -B build "-DCMAKE_C_COMPILER=$(which clang)" "-DCMAKE_CXX_COMPILER=$(which clang)" 20 | cmake --build build 21 | ``` 22 | 23 | ## Building (Windows) 24 | 25 | **Windows is currently not supported** 26 | 27 | **Important**: You need to use `clang-cl` to build the dependencies. Run the command below from a Visual Studio 2022 command prompt. 28 | 29 | ```sh 30 | cmake -G "NMake Makefiles" -DCMAKE_C_COMPILER=clang-cl.exe -DCMAKE_CXX_COMPILER=clang-cl.exe -B build 31 | cmake --build build 32 | ``` 33 | 34 | ## Debugging 35 | 36 | - If a build of a submodule fails and you want to force a full rebuild you can delete `build/-prefix` 37 | - For an external project you can delete `build/-prefix/src/-stamp` 38 | 39 | ## How it works 40 | 41 | This folder is a standalone CMake project that uses [ExternalProject_Add](https://cmake.org/cmake/help/latest/module/ExternalProject.html) to compile all dependencies statically into their own prefix. No system dependencies (except Git and Ninja) are used in the build process, which streamlines the build between platforms. 42 | 43 | The key is that every dependency's build process outputs a proper CMake package into the _prefix_. Take the capstone installation as an example: 44 | 45 | ``` 46 | ── bin 47 | │   └── cstool 48 | ├── include 49 | │   └── capstone 50 | │   ├── arm.h 51 | │   ├── arm64.h 52 | │   ├── bpf.h 53 | │   ├── capstone.h 54 | │   ├── evm.h 55 | │   ├── m680x.h 56 | │   ├── m68k.h 57 | │   ├── mips.h 58 | │   ├── mos65xx.h 59 | │   ├── platform.h 60 | │   ├── ppc.h 61 | │   ├── riscv.h 62 | │   ├── sparc.h 63 | │   ├── systemz.h 64 | │   ├── tms320c64x.h 65 | │   ├── wasm.h 66 | │   ├── x86.h 67 | │   └── xcore.h 68 | └── lib 69 | ├── cmake 70 | │   └── capstone 71 | │   ├── capstone-config-version.cmake 72 | │   ├── capstone-config.cmake 73 | │   ├── capstone-targets-noconfig.cmake 74 | │   └── capstone-targets.cmake 75 | ├── libcapstone.a 76 | └── pkgconfig 77 | └── capstone.pc 78 | ``` 79 | 80 | The files in `lib/cmake/capstone` allow you to link to capstone in your project's `CMakeLists.txt` like this: 81 | 82 | ```cmake 83 | cmake_minimum_required(VERSION 3.24) 84 | project(MyProject) 85 | 86 | find_package(capstone REQUIRED) 87 | 88 | add_executable(myproject src/main.cpp) 89 | target_link_libraries(myproject PRIVATE capstone::capstone) 90 | ``` 91 | 92 | If the `capstone` package is set up correctly this will propagate all requirements capstone has (build flags, C++ standard, defines, ...) to your `myproject` target. 93 | 94 | This `dependencies` project downloads, builds and installs all dependencies in `build/install` together. During the build of the dependencies the variable `CMAKE_PREFIX_PATH` is set to `build/install` as well, which is how recursive dependencies are handled. 95 | 96 | ## Updating a dependency 97 | 98 | To update a dependency like LLVM all you have to do is modify the corresponding CMake. See `CMakeLists.txt` for details. In this specific example you would modify the `URL` and `URL_HASH` in `llvm.cmake`: 99 | 100 | ```cmake 101 | ExternalProject_Add(llvm 102 | URL 103 | "https://github.com/llvm/llvm-project/releases/download/llvmorg-17.0.6/llvm-project-17.0.6.src.tar.xz" 104 | URL_HASH 105 | "SHA256=58a8818c60e6627064f312dbf46c02d9949956558340938b71cf731ad8bc0813" 106 | CMAKE_CACHE_ARGS 107 | ${CMAKE_ARGS} 108 | "-DLLVM_ENABLE_PROJECTS:STRING=clang;lld" 109 | "-DLLVM_ENABLE_ASSERTIONS:STRING=ON" 110 | "-DLLVM_ENABLE_DUMP:STRING=ON" 111 | "-DLLVM_ENABLE_RTTI:STRING=ON" 112 | "-DLLVM_ENABLE_LIBEDIT:STRING=OFF" 113 | "-DLLVM_PARALLEL_LINK_JOBS:STRING=1" 114 | "-DLLVM_ENABLE_DIA_SDK:STRING=OFF" 115 | CMAKE_GENERATOR 116 | "Ninja" 117 | SOURCE_SUBDIR 118 | "llvm" 119 | ) 120 | ``` 121 | 122 | For the `simple_git` helper and `ExternalProject_Add` with the `GIT_REPOSITORY` argument it is important to pin a specific _commit_ of a dependency (instead of a branch name like `master`). Doing this ensures that you will still be able to build a specific set of dependencies in the future. 123 | 124 | Another thing to be aware of is that the order of dependencies matters. Dependencies lower in the tree should be first, otherwise the dependencies higher up the tree will not build. 125 | 126 | ## Updating a dependency (submodule) 127 | 128 | Submodules are checked out in a [detached HEAD state](https://www.cloudbees.com/blog/git-detached-head). To make some changes the first thing you do it check out the branch you want to modify: 129 | 130 | ```sh 131 | cd dependencies/souper 132 | git checkout cmake-package 133 | ``` 134 | 135 | At this point you can change files in `dependencies/souper` however you wish (building the `dependencies` project will automatically build and install the updated version for you to test on the parent project). 136 | 137 | When you are happy with your changes you commit **and push** the changes: 138 | 139 | ```sh 140 | cd dependencies/souper 141 | git add myfiles 142 | git commit -m "My message" 143 | git push 144 | ``` 145 | 146 | Now all you did was modify the `souper` repository, but you still need to commit the changes to the submodule: 147 | 148 | ```sh 149 | git add dependencies/souper 150 | git commit -m "Updated souper" 151 | git push 152 | ``` 153 | 154 | Remember to tell the team because when they `git pull` they need to update the submodule and build the dependencies: 155 | 156 | ```sh 157 | git submodule update 158 | cmake -B dependencies/build -S dependencies 159 | cmake --build dependencies/build 160 | ``` 161 | 162 | ## Docker 163 | 164 | For convenience there is a [`Dockerfile`](./Dockerfile) provided. 165 | 166 | To build: 167 | 168 | ``` 169 | git submodule update --init 170 | docker buildx build --platform linux/arm64,linux/amd64 -t ghcr.io/mrexodia/cxx-common-cmake:latest . 171 | ``` 172 | 173 | Then push (maintainers only): 174 | 175 | ``` 176 | docker push ghcr.io/mrexodia/cxx-common-cmake:latest 177 | ``` 178 | 179 | Additionally generate the hash with `python hash.py` and push that tag: 180 | 181 | ``` 182 | docker tag ghcr.io/mrexodia/cxx-common-cmake:latest ghcr.io/mrexodia/cxx-common-cmake:20240808_b94d6786 183 | docker push ghcr.io/mrexodia/cxx-common-cmake:20240808_b94d6786 184 | ``` 185 | 186 | References: 187 | - https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/ 188 | - https://docs.docker.com/build/building/multi-stage/ 189 | 190 | ## GitHub Actions 191 | 192 | Below is an example `.github/workflows/build.yml` that uses `hash.py` to build and cache the dependencies (it assumes this repository was placed in the `dependencies` folder): 193 | 194 | ```yml 195 | name: build 196 | 197 | on: 198 | push: 199 | schedule: 200 | # Build master every 5 days to avoid costly cache rebuilds 201 | - cron: 0 0 */5 * * # https://crontab.guru/#0_0_*/5_*_* 202 | 203 | jobs: 204 | build: 205 | runs-on: ubuntu-22.04 206 | 207 | steps: 208 | - name: Checkout 209 | uses: actions/checkout@v3 210 | with: 211 | submodules: recursive 212 | 213 | - name: Install Ninja 214 | run: | 215 | if [ "$RUNNER_OS" == "Linux" ]; then 216 | sudo apt-get install -y ninja-build 217 | elif [ "$RUNNER_OS" == "macOS" ]; then 218 | brew install ninja 219 | elif [ "$RUNNER_OS" == "Windows" ]; then 220 | choco install ninja 221 | else 222 | echo "$RUNNER_OS not supported!" 223 | exit 1 224 | fi 225 | shell: bash 226 | 227 | - name: Hash Dependencies 228 | id: hash-dependencies 229 | run: | 230 | python dependencies/hash.py debug > $GITHUB_OUTPUT 231 | shell: bash 232 | 233 | - name: Cache Dependencies 234 | id: cache-dependencies 235 | uses: actions/cache@v3 236 | with: 237 | path: dependencies/build/install 238 | key: ${{ runner.os }}-${{ steps.hash-dependencies.outputs.file_hash }} 239 | restore-keys: | 240 | ${{ runner.os }}-${{ steps.hash-dependencies.outputs.restore_hash }} 241 | 242 | - name: Build Dependencies 243 | if: steps.cache-dependencies.outputs.cache-hit != 'true' 244 | run: | 245 | sudo ./ubuntu-dependencies.sh 246 | cmake -B dependencies/build -S dependencies "-DCMAKE_C_COMPILER=$(which clang)" "-DCMAKE_CXX_COMPILER=$(which clang)" 247 | cmake --build dependencies/build 248 | 249 | - name: Build Project 250 | run: | 251 | cmake -B build -G Ninja "-DCMAKE_BUILD_TYPE=Debug" "-DCMAKE_PREFIX_PATH=$(pwd)/dependencies/build/install" "-DCMAKE_C_COMPILER=$(which clang)" "-DCMAKE_CXX_COMPILER=$(which clang)" 252 | cmake --build build 253 | ``` 254 | --------------------------------------------------------------------------------