From 55c1a7f1c0d8b7b1f33d60dfcff6919db25c11a6 Mon Sep 17 00:00:00 2001 From: Sameer Rahmani Date: Sun, 19 Jun 2022 21:20:28 +0100 Subject: [PATCH] Add the initial build setup for libtooling --- .gitignore | 3 +- CMakeLists.txt | 232 +++++++++++++++++++++++++++++++++++ bin/CMakeLists.txt | 48 ++++++++ bin/leavitt.cpp | 52 ++++++++ builder | 235 ++++++++++++++++++++++++++++++++++++ include/leavitt.h | 22 ++++ include/leavitt/config.h.in | 8 ++ 7 files changed, 599 insertions(+), 1 deletion(-) create mode 100644 CMakeLists.txt create mode 100644 bin/CMakeLists.txt create mode 100644 bin/leavitt.cpp create mode 100755 builder create mode 100644 include/leavitt.h create mode 100644 include/leavitt/config.h.in diff --git a/.gitignore b/.gitignore index 74cae11..52499d5 100644 --- a/.gitignore +++ b/.gitignore @@ -100,4 +100,5 @@ Mkfile.old dkms.conf *~ -build/ \ No newline at end of file +build/ +.cache/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8d29637 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,232 @@ +# Leavitt - Source base todo manager for C family of languages +# +# Copyright (c) 2022 Sameer Rahmani +# +# 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, version 2. +# +# 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, see . +cmake_minimum_required(VERSION 3.19) + +# Project name and a few useful settings. Other commands can pick up the results +project(Leavitt + VERSION 1.0.0 + DESCRIPTION "Source base todo manager for C family of languages" + LANGUAGES CXX C) + +# Clangd command file +set(CMAKE_EXPORT_COMPILE_COMMANDS 1) +set(CMAKE_CXX_STANDARD 17 CACHE STRING "") + +# Policies ========================== +cmake_policy(SET CMP0116 OLD) + +# User Options ====================== +option(LEAVITT_BUILD_TESTING "Enable tests" OFF) +option(LEAVITT_ENABLE_BUILDID "Enable build id." OFF) +option(LEAVITT_ENABLE_THINLTO "Enable ThisLTO." ON) +option(LEAVITT_ENABLE_DOCS "Enable document generation" OFF) +option(LEAVITT_ENABLE_TIDY "Enable clang tidy check" OFF) +option(LEAVITT_DISABLE_CCACHE "Disable automatic ccache integration" OFF) + +# Only do these if this is the main project, and not if it is included through add_subdirectory +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + ## Settings ======================= + set(CMAKE_CXX_STANDARD_REQUIRED True) + + # Setup the source locations + set(INCLUDE_DIR ${CMAKE_SOURCE_DIR}/include) + + if(LEAVITT_ENABLE_TIDY) + find_program(CLANG_TIDY_PATH NAMES clang-tidy REQUIRED) + endif() + find_program(clang++ NAMES clang++ clang++ REQUIRED) + find_program(lld NAMES lld lld REQUIRED) + find_program(iwyu NAMES include-what-you-use iwyu REQUIRED) + + find_program(iwyu NAMES include-what-you-use iwyu REQUIRED) + set(iwyu_path ${iwyu}) + # Let's ensure -std=c++xx instead of -std=g++xx + set(CMAKE_CXX_EXTENSIONS OFF) + + set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/scripts/cmake") + set(MemoryCheckCommand "valgrind") + + configure_file(${INCLUDE_DIR}/leavitt/config.h.in include/leavitt/config.h) + + # Let's nicely support folders in IDEs + set_property(GLOBAL PROPERTY USE_FOLDERS ON) + + # Setup the basic compiler flags + add_compile_options( + -Wall + -Wextra + -Werror + -fno-rtti + -fno-builtin-strlen + + # Dedicate a section to each function, so the linker + # can do a better job on dead code elimination + -ffunction-sections + -fdata-sections + + $<$:-g3> + $<$:-ggdb> + # For the sake of debugging + $<$:-fno-inline> + # To make the local ccache happy + $<$:-fdebug-prefix-map=${PROJECT_SOURCE_DIR}=.> + + $<$:-fno-omit-frame-pointer> + $<$:-fomit-frame-pointer> + $<$:-fsanitize=address> + + $<$:-O3> + $<$:-fmerge-all-constants> + $<$:-flto=thin> + ) + + add_link_options( + # We enforce the lld linker + -fuse-ld=lld + -Wl,-gc-sections + $<$:-fsanitize-address-globals-dead-stripping> + $<$:-fsanitize=address> + $<$:-flto=thin> + $<$:-s> + # Do not link against shared libraries + #--static + ) + + if (LEAVITT_USE_COMPILER_RT) + add_link_options(-rtlib=compiler-rt -stdlib=libc++ ) + endif() + + find_program(LLD_PROGRAM REQUIRED NAMES lld) + + if(LEAVITT_ENABLE_BUILDID) + add_link_options(-Wl,--build-id) + endif() + + #TODO: Setup the THINLTO on release + if(LEAVITT_ENABLE_THINLTO) + endif() + + include(GNUInstallDirs) + Include(FetchContent) + + # CCache support ============================== + if(LEAVITT_DISABLE_CCACHE) + message(STATUS "CCache support is disabled") + else() + + find_program(CCACHE_PROGRAM ccache) + + if(CCACHE_PROGRAM) + message(STATUS "Found CCache") + set(LEAVITT_CCACHE_MAXSIZE "" CACHE STRING "Size of ccache") + set(LEAVITT_CCACHE_DIR "" CACHE STRING "Directory to keep ccached data") + set(LEAVITT_CCACHE_PARAMS "CCACHE_CPP2=yes CCACHE_HASHDIR=yes" + CACHE STRING "Parameters to pass through to ccache") + + set(CCACHE_PROGRAM "${LEAVITT_CCACHE_PARAMS} ${CCACHE_PROGRAM}") + + if (LEAVITT_CCACHE_MAXSIZE) + set(CCACHE_PROGRAM "CCACHE_MAXSIZE=${LEAVITT_CCACHE_MAXSIZE} ${CCACHE_PROGRAM}") + endif() + if (LEAVITT_CCACHE_DIR) + set(CCACHE_PROGRAM "CCACHE_DIR=${LEAVITT_CCACHE_DIR} ${CCACHE_PROGRAM}") + endif() + message(STATUS "Using CCACHE: ${CCACHE_PROGRAM}") + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_PROGRAM}) + else() + message(FATAL_ERROR "Unable to find the program ccache. Set LEAVITT_DISABLE_CCACHE to ON") + endif() + endif() + + + if(LEAVITT_BUILD_TESTING) + message(STATUS "Fetching Googletest...") + FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip + ) + # For Windows: Prevent overriding the parent project's compiler/linker settings + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) + endif() + + # Clang setup ========================================= + + if(DEFINED Clang_ROOT) + set(CT_CLANG_PACKAGE_DIR "${Clang_ROOT}/../../..") + elseif(DEFINED Clang_DIR) + set(CT_CLANG_PACKAGE_DIR "${Clang_DIR}/../../..") + endif() + + mark_as_advanced(CT_CLANG_PACKAGE_DIR) + + # Set this to a valid Clang installation directory. This is most likely where + # LLVM is installed on your system. + set(CT_Clang_INSTALL_DIR "${CT_CLANG_PACKAGE_DIR}" CACHE PATH + "Clang installation directory") + + set(CT_LLVM_INCLUDE_DIR "${CT_Clang_INSTALL_DIR}/include/llvm") + if(NOT EXISTS "${CT_LLVM_INCLUDE_DIR}") + message(FATAL_ERROR + " CT_Clang_INSTALL_DIR (${CT_LLVM_INCLUDE_DIR}) is invalid.") + endif() + + set(CT_LLVM_CMAKE_FILE + "${CT_Clang_INSTALL_DIR}/lib/cmake/clang/ClangConfig.cmake") + if(NOT EXISTS "${CT_LLVM_CMAKE_FILE}") + message(FATAL_ERROR + " CT_LLVM_CMAKE_FILE (${CT_LLVM_CMAKE_FILE}) is invalid.") + endif() + + list(APPEND CMAKE_PREFIX_PATH "${CT_Clang_INSTALL_DIR}/lib/cmake/clang/") + + find_package(Clang REQUIRED CONFIG) + message(STATUS "Found Clang ${CLANG_PACKAGE_VERSION}") + + if("${LLVM_VERSION_MAJOR}" LESS "15") + message(FATAL_ERROR "Found LLVM ${LLVM_VERSION_MAJOR}, but need LLVM 13") + endif() + + message(STATUS "Found Clang ${LLVM_PACKAGE_VERSION}") + message(STATUS "Using ClangConfig.cmake in: ${CT_Clang_INSTALL_DIR}") + + message("CLANG STATUS: + Includes (clang) ${CLANG_INCLUDE_DIRS} + Includes (llvm) ${LLVM_INCLUDE_DIRS}" + ) + + # Set the LLVM and Clang header and library paths + include_directories(SYSTEM "${LLVM_INCLUDE_DIRS};${CLANG_INCLUDE_DIRS}") + + + # Leavitt Setup =================================== + + include_directories(SYSTEM ${PROJECT_BINARY_DIR}/include) + + add_subdirectory(bin) + + if (LEAVITT_ENABLE_DOCS) + add_subdirectory(docs) + endif() + + install(DIRECTORY ${PROJECT_BINARY_DIR}/include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING + PATTERN *.h + PATTERN *.td + PATTERN "CMake*" EXCLUDE) + +endif() diff --git a/bin/CMakeLists.txt b/bin/CMakeLists.txt new file mode 100644 index 0000000..7c62ea8 --- /dev/null +++ b/bin/CMakeLists.txt @@ -0,0 +1,48 @@ +# Leavitt - Source base todo manager for C family of languages +# +# Copyright (c) 2022 Sameer Rahmani +# +# 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, version 2. +# +# 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, see . + +add_executable(leavitt leavitt.cpp) + +set_target_properties(leavitt PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + # Warn on unused libs + LINK_WHAT_YOU_USE TRUE + CXX_INCLUDE_WHAT_YOU_USE "${iwyu_path}" + C_INCLUDE_WHAT_YOU_USE "${iwyu_path}" + + # LTO support + INTERPROCEDURAL_OPTIMIZATION TRUE) + +if(LEAVITT_ENABLE_TIDY) + set_target_properties(leavitt PROPERTIES CXX_CLANG_TIDY ${CLANG_TIDY_PATH}) +endif() + +target_compile_features(leavitt PRIVATE cxx_std_17) + + +target_link_libraries(leavitt + PRIVATE + + clangTooling + ) + +target_include_directories(leavitt PRIVATE ${PROJECT_BINARY_DIR}) +target_include_directories(leavitt PRIVATE ${INCLUDE_DIR}) + + +install(TARGETS leavitt + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/bin/leavitt.cpp b/bin/leavitt.cpp new file mode 100644 index 0000000..53763c2 --- /dev/null +++ b/bin/leavitt.cpp @@ -0,0 +1,52 @@ +/* + * Leavitt - Source base todo manager for C family of languages + * + * Copyright (c) 2019-2022 Sameer Rahmani + * + * 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, version 2. + * + * 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, see . + */ +#include +#include +#include +#include + +using namespace clang::tooling; + +// Apply a custom category to all command-line options so that they are the +// only ones displayed. +static llvm::cl::OptionCategory LeavittCategory("Leavitt options"); + +// CommonOptionsParser declares HelpMessage with a description of the common +// command-line options related to the compilation database and input files. +// It's nice to have this help message in all tools. +static llvm::cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); + +// A help message for this specific tool can be added afterwards. +static llvm::cl::extrahelp MoreHelp("\nMore help text...\n"); + +int main(int argc, const char *argv[]) { + auto ExpectedParser = + CommonOptionsParser::create(argc, argv, LeavittCategory); + if (!ExpectedParser) { + // Fail gracefully for unsupported options. + llvm::errs() << ExpectedParser.takeError(); + return 1; + } + + CommonOptionsParser &OptionsParser = ExpectedParser.get(); + // Use OptionsParser.getCompilations() and OptionsParser.getSourcePathList() + // to retrieve CompilationDatabase and the list of input file paths. + ClangTool Tool(OptionsParser.getCompilations(), + OptionsParser.getSourcePathList()); + return Tool.run(newFrontendActionFactory().get()); +} diff --git a/builder b/builder new file mode 100755 index 0000000..112c5a0 --- /dev/null +++ b/builder @@ -0,0 +1,235 @@ +#! /bin/bash +# Leavitt - Source base todo manager for C family of languages +# +# Copyright (c) 2022 Sameer Rahmani +# +# 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, version 2. +# +# 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, see . + +# ----------------------------------------------------------------------------- +# Commentary +# ----------------------------------------------------------------------------- +# This is the builder script for the Leavitt project. It makes it easier to +# interact with the CMake build scripts. +# +# In order to define a subcommand all you need to do is to define a function +# with the following syntax: +# +# function subcommand-name() { ## DESCRIPTION +# .. subcommand body .. +# } +# +# Make sure to provid one line of DESCRIPTION for the subcommand and use two "#" +# characters to start the description following by a space. Otherwise, your +# subcommand won't be registered + +set -e + +# ----------------------------------------------------------------------------- +# Vars & Config +# ----------------------------------------------------------------------------- + +command=$1 +VERSION="0.5.0" + +CC=$(which clang) +CXX=$(which clang++) + +export CC +export CXX + +CLANG_DIR="$(dirname "$CXX")/../" + +# TODO: Add sloppiness to the cmake list file as well +export CCACHE_SLOPPINESS="pch_defines,time_macros" + +export ASAN_OPTIONS=check_initialization_order=1 +LSAN_OPTIONS=suppressions=$(pwd)/.ignore_sanitize +export LSAN_OPTIONS +export LDFLAGS="-fuse-ld=lld" + +# The `builder` script is supposed to be run from the +# root of the source tree +ME=$(cd "$(dirname "$0")/." >/dev/null 2>&1 ; pwd -P)ME=$(cd "$(dirname "$0")/." >/dev/null 2>&1 ; pwd -P) +ROOT_DIR=$ME +BUILD_DIR=$ROOT_DIR/build + +CMAKEARGS_DEBUG=("-DCMAKE_BUILD_TYPE=Debug") + # Verbose -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON +CMAKEARGS=("-DCMAKE_EXPORT_COMPILE_COMMANDS=ON" + "-DLEAVITT_CCACHE_DIR=$HOME/.ccache" + "-DCT_Clang_INSTALL_DIR=$CLANG_DIR") + +# ----------------------------------------------------------------------------- +# Helper functions +# ----------------------------------------------------------------------------- +function fn-names() { + grep -E '^function [0-9a-zA-Z_-]+\(\) \{ ## .*$$' "$0" | sed 's/^function \([a-zA-Z0-9_-]*\)() { ## \(.*\)/\1/' +} + +function pushed_build() { + mkdir -p "$BUILD_DIR" + pushd "$BUILD_DIR" > /dev/null || return +} + + +function popd_build() { + popd > /dev/null || return +} + + +function build-gen() { + pushed_build + info "Running: " + info "cmake -G Ninja ${CMAKEARGS[*]} ${CMAKEARGS_DEBUG=[*]}" "\"$*\" \"$ROOT_DIR\"" + cmake -G Ninja "${CMAKEARGS[@]}" "${CMAKEARGS_DEBUG[@]}" "$*" "$ROOT_DIR" + popd_build +} + + +function info() { + if [ "$1" ] + then + echo -e "[\033[01;32mINFO\033[00m]: $*" + fi +} + +function error() { + if [ "$1" ] + then + echo -e "[\033[01;31mERR\033[00m]: $*" + fi +} + +function warn() { + if [ "$1" ] + then + echo -e "[\033[01;33mWARN\033[00m]: $*" + fi +} + +# ----------------------------------------------------------------------------- +# Subcomaands +# ----------------------------------------------------------------------------- +function compile() { ## Compiles the project using the generated build scripts + pushed_build + cmake --build . + popd_build +} + +function build() { ## Builds the project by regenerating the build scripts + local cpus + + clean + build-gen "$@" + pushed_build + + cpus=$(grep ^cpu\\scores /proc/cpuinfo | uniq | awk '{print $4}') + cmake --build . -j "$cpus" + + popd_build +} + +function build-tidy() { ## Builds the project using clang-tidy (It takes longer than usual) + build "-DLEAVITT_ENABLE_TIDY=ON" "${@:2}" +} + +function build-release() { ## Builds the project in "Release" mode + clean + pushed_build + cmake -G Ninja -DCMAKE_BUILD_TYPE=Release "$ROOT_DIR" + cmake --build . + popd_build +} + +function build-docs() { ## Builds the documentation of Leavitt + clean + pushed_build + cmake -G Ninja -DLEAVITT_ENABLE_DOCS=ON "$ROOT_DIR" + cmake --build . + popd_build +} + +function clean() { ## Cleans up the source dir and removes the build + rm -rf "$BUILD_DIR" + rm -rf "$(find . -iname '*~')" +} + +function run() { ## Runs `leavittc` and passes all the given aruguments to it + LD_PRELOAD=$(clang -print-file-name=libclang_rt.asan-x86_64.so) \ + "$BUILD_DIR"/bin/leavitt "$@" +} + +function memcheck-leavitt() { ## Runs `valgrind` to check `leavittc` birany + export ASAN_FLAG="" + build + pushed_build + valgrind --tool=memcheck --leak-check=yes --trace-children=yes "$BUILD_DIR"/bin/leavitt "$@" + popd_build +} + + +function build-tests() { ## Generates and build the project including the test cases + clean + pushed_build + cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DLEAVITT_BUILD_TESTING=ON "$ROOT_DIR" + cmake --build . + popd_build +} + +function setup() { ## Setup the working directory and make it ready for development + if command -v python3 >/dev/null 2>&1; then + pip install pre-commit + pre-commit install + else + error "Python is required to setup pre-commit" + fi +} + +function scan-build() { ## Runs the `scan-build` utility to analyze the build process + clean + build-gen + pushed_build + # The scan-build utility scans the build for bugs checkout the man page + scan-build --force-analyze-debug-code --use-analyzer="$CC" cmake --build . + popd_build +} + +function help() { ## Print out this help message + echo "Commands:" + grep -E '^function [a-zA-Z0-9_-]+\(\) \{ ## .*$$' "$0" | \ + sort | \ + sed 's/^function \([a-zA-Z0-9_-]*\)() { ## \(.*\)/\1:\2/' | \ + awk 'BEGIN {FS=":"}; {printf "\033[36m%-30s\033[0m %s\n", $1, $2}' +} + +# ----------------------------------------------------------------------------- +# Main logic +# ----------------------------------------------------------------------------- +echo -e "\nLeavitt Builder Version $VERSION" +echo -e "\nCopyright (C) 2022" +echo -e "Sameer Rahmani " +echo -e "Leavitt comes with ABSOLUTELY NO WARRANTY;" +echo -e "This is free software, and you are welcome" +echo -e "to redistribute it under certain conditions;" +echo -e "for details take a look at the LICENSE file.\n" + +# Find the subcommand in the functions and run we find it. +for fn in $(fn-names); do + if [[ $fn == "$command" ]]; then + eval "$fn ${*:2}" + exit $? + fi +done + +# If we couldn't find the command print out the help message +help diff --git a/include/leavitt.h b/include/leavitt.h new file mode 100644 index 0000000..f63048b --- /dev/null +++ b/include/leavitt.h @@ -0,0 +1,22 @@ +/* -*- C++ -*- + * Leavitt - Source base todo manager for C family of languages + * + * Copyright (c) 2022 Sameer Rahmani + * + * 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, version 2. + * + * 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, see . + */ + +#ifndef LEAVITT_H +#define LEAVITT_H +namespace leavitt {} +#endif diff --git a/include/leavitt/config.h.in b/include/leavitt/config.h.in new file mode 100644 index 0000000..6f67025 --- /dev/null +++ b/include/leavitt/config.h.in @@ -0,0 +1,8 @@ +#ifndef CONFIG_H +#define CONFIG_H + +// the configured options and settings + +#define LEAVITT_VERSION "@PROJECT_VERSION@" + +#endif