Add the initial build setup for libtooling

This commit is contained in:
Sameer Rahmani 2022-06-19 21:20:28 +01:00
parent b689ed16f2
commit 55c1a7f1c0
7 changed files with 599 additions and 1 deletions

1
.gitignore vendored
View File

@ -101,3 +101,4 @@ dkms.conf
*~
build/
.cache/

232
CMakeLists.txt Normal file
View File

@ -0,0 +1,232 @@
# Leavitt - Source base todo manager for C family of languages
#
# Copyright (c) 2022 Sameer Rahmani <lxsameer@gnu.org>
#
# 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 <http://www.gnu.org/licenses/>.
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
$<$<CONFIG:DEBUG>:-g3>
$<$<CONFIG:DEBUG>:-ggdb>
# For the sake of debugging
$<$<CONFIG:DEBUG>:-fno-inline>
# To make the local ccache happy
$<$<CONFIG:DEBUG>:-fdebug-prefix-map=${PROJECT_SOURCE_DIR}=.>
$<$<CONFIG:DEBUG>:-fno-omit-frame-pointer>
$<$<CONFIG:RELEASE>:-fomit-frame-pointer>
$<$<CONFIG:DEBUG>:-fsanitize=address>
$<$<CONFIG:RELEASE>:-O3>
$<$<CONFIG:RELEASE>:-fmerge-all-constants>
$<$<CONFIG:RELEASE>:-flto=thin>
)
add_link_options(
# We enforce the lld linker
-fuse-ld=lld
-Wl,-gc-sections
$<$<CONFIG:RELEASE>:-fsanitize-address-globals-dead-stripping>
$<$<CONFIG:DEBUG>:-fsanitize=address>
$<$<CONFIG:RELEASE>:-flto=thin>
$<$<CONFIG:RELEASE>:-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()

48
bin/CMakeLists.txt Normal file
View File

@ -0,0 +1,48 @@
# Leavitt - Source base todo manager for C family of languages
#
# Copyright (c) 2022 Sameer Rahmani <lxsameer@gnu.org>
#
# 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 <http://www.gnu.org/licenses/>.
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})

52
bin/leavitt.cpp Normal file
View File

@ -0,0 +1,52 @@
/*
* Leavitt - Source base todo manager for C family of languages
*
* Copyright (c) 2019-2022 Sameer Rahmani <lxsameer@gnu.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <clang/Frontend/FrontendActions.h>
#include <clang/Tooling/CommonOptionsParser.h>
#include <clang/Tooling/Tooling.h>
#include <llvm/Support/CommandLine.h>
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<clang::SyntaxOnlyAction>().get());
}

235
builder Executable file
View File

@ -0,0 +1,235 @@
#! /bin/bash
# Leavitt - Source base todo manager for C family of languages
#
# Copyright (c) 2022 Sameer Rahmani <lxsameer@gnu.org>
#
# 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 <http://www.gnu.org/licenses/>.
# -----------------------------------------------------------------------------
# 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 <lxsameer@gnu.org>"
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

22
include/leavitt.h Normal file
View File

@ -0,0 +1,22 @@
/* -*- C++ -*-
* Leavitt - Source base todo manager for C family of languages
*
* Copyright (c) 2022 Sameer Rahmani <lxsameer@gnu.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef LEAVITT_H
#define LEAVITT_H
namespace leavitt {}
#endif

View File

@ -0,0 +1,8 @@
#ifndef CONFIG_H
#define CONFIG_H
// the configured options and settings
#define LEAVITT_VERSION "@PROJECT_VERSION@"
#endif