Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

# Directories
src/.bin
cxx/build
.cache/
**/__pycache__
.pytest_cache
Expand Down
137 changes: 137 additions & 0 deletions cxx/.ycm_extra_conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"""
YCM Config for gitstatus.
"""
import os
import ycm_core

ROOT = os.path.abspath(os.path.dirname(__file__))
if os.path.dirname(__file__) == '':
ROOT = os.getcwd()

# Extensions that will be looked for.
SOURCE_EXTENSIONS = ['.c', '.cpp', '.cxx', '.cc', '.m', '.mm']
HEADER_EXTENSIONS = ['.h', '.hpp', '.hxx', '.hh']

# These are the compilation flags that will be used by YCM to check c files.
FLAGS = [
# C Flags
# Debug options: https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html
'-ggdb',
'-Og',
'-Wall',
'-Wextra',
'-Werror=format-security',
'-Wundef',
'-Winline',
'-Wshadow',
'-Wunused',
'-Winit-self',
'-pedantic', # Warn if violating std, pedantic-errors makes it an error
'-pipe',
'-fexceptions',
'-Weffc++',
# Defines
'-D_FORTIFY_SOURCE=2',
'-D_GLIBCXX_ASSERTIONS',
'-std=c++11',
# Need to tell clang language of headers.
# For a C project set to 'c' instead of 'c++'.
'-x',
'c++',
# Includes.
'-isystem',
'/usr/include',
'-isystem',
'/usr/include/c++/8',
'-isystem',
'/usr/local/include',
]

# Set this to the absolute path to the folder (NOT the file!) containing the
# compile_commands.json file to use that instead of 'flags'. See here for
# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
#
# Most projects will NOT need to set this to anything; you can just change the
# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
COMPILATION_DB_FOLDER = os.path.join(ROOT, 'build')
DB = None
if os.path.isdir(COMPILATION_DB_FOLDER):
DB = ycm_core.CompilationDatabase(COMPILATION_DB_FOLDER)


def MakeRelativePathsInFlagsAbsolute(flags, working_directory):
""" Processes paths in FLAGS var and makes them absolute. """
if not working_directory:
return list(flags)
new_flags = []
make_next_absolute = False
path_flags = ['-isystem', '-I', '-iquote', '--sysroot=']
for flag in flags:
new_flag = flag

if make_next_absolute:
make_next_absolute = False
if not flag.startswith('/'):
new_flag = os.path.join(working_directory, flag)

for path_flag in path_flags:
if flag == path_flag:
make_next_absolute = True
break

if flag.startswith(path_flag):
path = flag[len(path_flag):]
new_flag = path_flag + os.path.join(working_directory, path)
break

if new_flag:
new_flags.append(new_flag)
return new_flags


def IsHeaderFile(filename):
""" Return true only if filename ends in a header extension. """
extension = os.path.splitext(filename)[1]
return extension in HEADER_EXTENSIONS


def GetCompilationInfoForFile(filename):
""" The compilation_commands.json file generated by CMake does not have
entries for header files. So we do our best by asking the db for
flags for a corresponding source file, if any. If one exists, the
flags for that file should be good enough.
"""
if IsHeaderFile(filename):
basename = os.path.splitext(filename)[0]
for extension in SOURCE_EXTENSIONS:
replacement_file = basename + extension
if os.path.exists(replacement_file):
compilation_info = DB.GetCompilationInfoForFile(
replacement_file)
if compilation_info.compiler_flags_:
return compilation_info
return None
return DB.GetCompilationInfoForFile(filename)


def FlagsForFile(filename, **kwargs):
""" Given a filename, return the flags to compile it. """
with open("/tmp/ttt", 'w') as fout:
if DB:
# Bear in mind that compilation_info.compiler_flags_ does NOT return a
# python list, but a "list-like" StringVec object
compilation_info = GetCompilationInfoForFile(filename)
fout.write("comp_info" + str(compilation_info) + "\n")
if not compilation_info:
return None

final_flags = MakeRelativePathsInFlagsAbsolute(
compilation_info.compiler_flags_,
compilation_info.compiler_working_dir_)
fout.write("final_flags" + str(final_flags))

else:
relative_to = os.path.dirname(os.path.abspath(__file__))
final_flags = MakeRelativePathsInFlagsAbsolute(FLAGS, relative_to)

return {'flags': final_flags, 'do_cache': True}
95 changes: 95 additions & 0 deletions cxx/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
cmake_minimum_required (VERSION 3.1.0)
project(gitstatus)

# Pregenerate step in build, generate constants before compilation
add_executable(gen_const src/gen_const.cc)
# add the command to generate the source code
add_custom_command (
OUTPUT ${CMAKE_BINARY_DIR}/src/const.h
COMMAND gen_const ${CMAKE_BINARY_DIR}/src/const.h
DEPENDS gen_const
)

# Build core project
add_library(gstat src/gstat.cc ${CMAKE_BINARY_DIR}/src/const.h)
add_executable(gitstatus src/main.cc)
target_link_libraries(gitstatus gstat)

# Define ${CMAKE_INSTALL_...} variables
include(GNUInstallDirs)

# Specify directories required
link_directories(${CMAKE_BINARY_DIR}/lib/installed/${CMAKE_INSTALL_LIBDIR})
include_directories(
${CMAKE_BINARY_DIR}/lib/installed/${CMAKE_INSTALL_INCLUDEDIR}
${CMAKE_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR} # Make project includes from root
)

# Optional requirement for test suite
find_package(Threads)
if(${Threads_FOUND})
# Fetch googletest inside build dir
include(ExternalProject)
ExternalProject_Add(googletest
PREFIX "${CMAKE_BINARY_DIR}/lib"
GIT_REPOSITORY "https://github.com/google/googletest.git"
GIT_TAG "master"
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/lib/installed
)

# Prevent build on all targets build
set_target_properties(googletest PROPERTIES EXCLUDE_FROM_ALL TRUE)

# Test
add_executable(tests test/test_gstat.cc)
target_link_libraries(tests gstat gtest Threads::Threads)

# Make sure third-party is built before executable
add_dependencies(tests googletest)
set_target_properties(tests PROPERTIES EXCLUDE_FROM_ALL TRUE)

# Add post build running of tests
add_custom_command(
TARGET tests
COMMENT "Run google tests post build"
POST_BUILD
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMAND "./tests" "--gtest_color=yes"
)
endif()

# Flags & compiler
set(CMAKE_CXX_FLAGS "-std=c++11 -pedantic -pipe -fexceptions -Winline")
if(CMAKE_BUILD_TYPE STREQUAL "Release")
add_definitions(-D_FORTIFY_SOURCE=2)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Ofast")
else()

add_definitions(-D_FORTIFY_SOURCE=2 -D_GLIBCXX_ASSERTIONS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -Og -Wall -Wextra -Werror=format-security")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wshadow -Wunused -Winit-self -Weffc++")
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg") # GProf
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") # GCov
endif()

# Will export build commands to build dir, for YCM users
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Just for coverage, still not working
# TODO: Make it go!
# set(CMAKE_CXX_FLAGS_COVERAGE "-ggdb -O0 --coverage -fprofile-arcs -ftest-coverage -Wall -Wextra")
# if(CMAKE_BUILD_TYPE STREQUAL "Coverage")
# set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/scripts/cmake)
# include(CodeCoverage)
# APPEND_COVERAGE_COMPILER_FLAGS()
# setup_target_for_coverage(NAME coverage EXECUTABLE tests DEPENDENCIES googletest)

# # set(CMAKE_CXX_FLAGS_COVERAGE "-ggdb -O0 --coverage -fprofile-arcs -ftest-coverage -Wall -Wextra")
# # SET(CMAKE_CXX_FLAGS "-g -O0 --coverage -fprofile-arcs -ftest-coverage")
# # SET(CMAKE_C_FLAGS "-g -O0 --coverage -fprofile-arcs -ftest-coverage")
# endif(CMAKE_BUILD_TYPE STREQUAL "Coverage")

# TODO: Better way?
# set(CMAKE_CXX_STANDARD 11)
# set(CMAKE_CXX_STANDARD_REQUIRED ON)
21 changes: 21 additions & 0 deletions cxx/LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License

Copyright (c) 2018 Jeremy Pallats/starcraft.man

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
21 changes: 21 additions & 0 deletions cxx/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Gitstatus (C++ Implementation)

Default build is debug.

## Requirements

1. `cmake`, version >= 3.1.0
2. C++ compiler implementing c++11 standard, for example GCC version >= 5.0
3. pthread library for google tests

## To Build

1. `mkdir ./build && cd build`
1. `cmake -DCMAKE_BUILD_TYPE=Release .. && make`

For debug version, just omit -DCMAKE_BUILD_TYPE flag.

## To Test

1. `mkdir ./build && cd build`
1. `cmake .. && make tests`
16 changes: 16 additions & 0 deletions cxx/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/sh
BUILD_D="$(dirname "$(readlink -f "$0")")/build"

echo "This will build the c++ based version of gitstatus."
echo "To build, both cmake and a C++11 compliant compiler are required."
echo "-----------------------------------------------------------------"

command rm -rf "$BUILD_D"
command mkdir -p "$BUILD_D"
cd "$BUILD_D"
cmake .. -DCMAKE_BUILD_TYPE=Release
make

echo "-----------------------------------------------------------------"
echo "To use the cxx version, put following in your ~/.zshrc"
echo " export GIT_PROMPT_EXECUTABLE=cxx"
67 changes: 67 additions & 0 deletions cxx/src/gen_const.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* The MIT License
*
* Copyright (c) 2018 Jeremy Pallats/starcraft.man
*
* Generate a simple header with predefined hashes for runtime use.
*/
#include <sys/stat.h>

#include <cctype>
#include <cstdint>
#include <fstream>
#include <initializer_list>
#include <iostream>
#include <string>

#include "src/gstat.h"

#define HEADER_GUARD "CXX_BUILD_SRC_CONST_H_"

/**
* Just a simple mkdir clone, recurse until exists.
*
* Raises: std::runtime_error - Unable to complete mkdir
*/
void mkdir_recurse(const std::string &path) {
if (path.length() == 0 || path == ROOT_DIR || gstat::file_is_dir(path)) {
return;
}

mkdir_recurse(gstat::dirname(path));

uint_fast8_t err = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (err != 0) {
throw std::runtime_error("Unable to create dir: " + path);
}
}

int main(int argc, char *argv[]) {
if (argc != 2) {
std::cout << "Usage: " << argv[0] << " filename" << std::endl;
return 1;
}

std::string fname(argv[1]);
mkdir_recurse(gstat::dirname(fname));

std::ofstream fout(fname.c_str());
if (!fout.good()) {
std::cout << "CRITIAL ERROR: Cannot open file!" << std::endl;
return 1;
}

fout << "#ifndef " << HEADER_GUARD << std::endl
<< "#define " << HEADER_GUARD << std::endl <<std::endl;

std::initializer_list<std::string> vals({ "AA", "AU", "DD", "DU", "UA", "UD", "UU" });
for (std::initializer_list<std::string>::const_iterator itr = vals.begin(); itr != vals.end(); ++itr) {
fout << "#define HASH_CASE_" << *itr << " "
<< gstat::hash_two_places(*itr) << std::endl;
}

fout << std::endl << "#endif // " << HEADER_GUARD << std::endl;
fout.close();

return 0;
}
Loading