Skip to content
Merged
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
42 changes: 42 additions & 0 deletions .githooks/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env bash

# The file containing the temporary commit message
MSG_FILE=$1
COMMIT_TITLE=$(head -n 1 "$MSG_FILE")

# ANSI colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m' # No Color

# Standard definition
TYPES="(FEAT|FIX|BUG|QA|REFACTOR|PERF|DOCS|TEST|CHORE)"
MODULES="(ASSEMBLY|CORE|GEOM|MATH|IO|SYM|MESH|HPC)"
PATTERN="^${TYPES}\(${MODULES}\): .+"

if [[ ! $COMMIT_TITLE =~ $PATTERN ]]; then
echo -e "${RED}${BOLD}===============================================================${NC}"
echo -e "${RED}${BOLD} ERROR: Invalid commit message format.${NC}"
echo -e "${RED}${BOLD}===============================================================${NC}"
echo -e " Your message : '${YELLOW}$COMMIT_TITLE${NC}'\n"
echo -e " Required format is: ${CYAN}${BOLD}TYPE(MODULE): description${NC} (in lowercase)\n"
echo -e " Allowed ${CYAN}TYPES${NC}:"
echo -e " ${GREEN}FEAT${NC} : New feature (e.g., new element type)"
echo -e " ${GREEN}FIX / BUG${NC}: Bug fix or crash resolution (e.g., fixed segfault)"
echo -e " ${GREEN}QA${NC} : Quality Assurance (assertions, error handling)"
echo -e " ${GREEN}REFACTOR${NC} : Code restructuring without behavior change"
echo -e " ${GREEN}PERF${NC} : HPC Optimization (SIMD, Arena, Caching)"
echo -e " ${GREEN}DOCS${NC} : Documentation additions or changes (Doxygen)"
echo -e " ${GREEN}TEST${NC} : Adding unit tests (GTest)"
echo -e " ${GREEN}CHORE${NC} : Maintenance tasks (CMake, scripts, cleanup)\n"
echo -e " Allowed ${CYAN}MODULES${NC}:"
echo -e " ${YELLOW}ASSEMBLY, CORE, GEOM, MATH, IO, SYM, MESH, HPC${NC}\n"
echo -e " Valid example: ${GREEN}PERF(HPC): implement zero-copy Arena allocator${NC}"
echo -e "${RED}${BOLD}===============================================================${NC}"
exit 1
fi

exit 0
21 changes: 21 additions & 0 deletions .githooks/post-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash

# ANSI colors
GREEN='\033[0;32m'
CYAN='\033[0;36m'
YELLOW='\033[1;33m'
BOLD='\033[1m'
NC='\033[0m' # No Color

echo ""
echo -e "${CYAN}${BOLD}========================================================================${NC}"
echo -e "${GREEN}${BOLD} SUCCESS: Commit successfully recorded! ${NC}"
echo -e "${CYAN}${BOLD}========================================================================${NC}"
echo -e " The CFEM++ code is clean, the includes are perfectly sorted,"
echo -e " and the message strictly follows industrial standards."
echo -e ""
echo -e " ${YELLOW}${BOLD}Excellent work!${NC}"
echo -e "${CYAN}${BOLD}========================================================================${NC}"
echo ""

exit 0
168 changes: 168 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
#!/usr/bin/env python3
import sys
import subprocess

# ANSI colors for Python
RED = '\033[91m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
CYAN = '\033[96m'
BOLD = '\033[1m'
RESET = '\033[0m'

COPYRIGHT_HEADER = [
"//************************************************************************",
"// --- CFEM++ Library",
"// ---",
"// --- Copyright 2024-2026 Ismaël Tchinda Ngueyong et al.",
"// --- ALL RIGHTS RESERVED",
"// ---",
"// --- This software is protected by international copyright laws.",
"// --- Use, distribution, or modification of this software in any form,",
"// --- source or binary, for personal, academic, or non-commercial ",
"// --- purposes is permitted free of charge, provided that this ",
"// --- copyright notice and this permission notice appear in all ",
"// --- copies and supporting documentation.",
"// ---",
"// --- The authors and contributors provide this code \"as is\" without ",
"// --- any express or implied warranty. In no event shall they be ",
"// --- held liable for any damages arising from the use of this software.",
"//************************************************************************"
]

def get_staged_cpp_files():
"""Retrieves modified C++ files staged for commit."""
result = subprocess.run(['git', 'diff', '--cached', '--name-only', '--diff-filter=ACM'],
capture_output=True, text=True)
files = result.stdout.split('\n')
return [f for f in files if f.endswith(('.h', '.hpp', '.cpp', '.cc', '.cxx', '.tpp'))]

def check_copyright(filepath, lines):
"""Checks if the file starts with the exact copyright header."""
if len(lines) < len(COPYRIGHT_HEADER):
has_error = True
else:
has_error = False
# Compare line by line, stripping trailing whitespaces/newlines to avoid OS formatting issues
for i, expected_line in enumerate(COPYRIGHT_HEADER):
if lines[i].rstrip() != expected_line.rstrip():
has_error = True
break

if has_error:
print(f"\n{RED}{BOLD} STANDARD ERROR: Missing or invalid copyright header in '{filepath}'{RESET}")
print(f" {YELLOW}Please place the following exact text at the very top of the file:{RESET}\n")
for line in COPYRIGHT_HEADER:
print(f"{GREEN}{line}{RESET}")
print(f"\n {CYAN}-------------------------------------------{RESET}")
return True

return False

def check_includes(filepath, lines):
"""Smartly checks, groups, and sorts includes in the header block."""
start_idx = -1
end_idx = -1
includes = []

# Extract the main include block at the top of the file
for i, line in enumerate(lines):
stripped = line.strip()
if stripped.startswith('#include'):
if start_idx == -1:
start_idx = i
end_idx = i
includes.append(stripped)
elif not stripped or stripped.startswith('//') or stripped.startswith('/*') or stripped.startswith('*'):
continue # Skip empty lines and comments (like the copyright)
elif stripped.startswith('#pragma') or stripped.startswith('#ifndef') or stripped.startswith('#define'):
continue # Skip header guards
else:
# We hit real code. Stop gathering includes.
if start_idx != -1:
break

if not includes:
return False

# Separate and remove duplicates using Sets
sys_includes = set()
loc_includes = set()

for inc in includes:
if '<' in inc and '>' in inc:
sys_includes.add(inc)
else:
loc_includes.add(inc)

# Sort intelligently (case-insensitive primary, case-sensitive secondary)
sys_list = sorted(list(sys_includes), key=lambda x: (x.lower(), x))
loc_list = sorted(list(loc_includes), key=lambda x: (x.lower(), x))

# Build the ideal block
ideal_block = []
if sys_list:
ideal_block.extend(sys_list)
if sys_list and loc_list:
ideal_block.append("") # Elegant empty line between system and local
if loc_list:
ideal_block.extend(loc_list)

ideal_str = "\n".join(ideal_block)

# Reconstruct the current block to compare
current_clean = []
for line in lines[start_idx:end_idx+1]:
s = line.strip()
if s.startswith('#include'):
current_clean.append(s)
elif s == '' and current_clean and current_clean[-1] != '':
current_clean.append('') # Keep only one empty line maximum

while current_clean and current_clean[-1] == '':
current_clean.pop()

current_str = "\n".join(current_clean)

# Compare and output
if ideal_str != current_str:
print(f"\n{RED}{BOLD} STANDARD ERROR: Unsorted, messy, or duplicate includes in '{filepath}'{RESET}")
print(f" {CYAN}--- Lines {start_idx + 1} to {end_idx + 1} ---{RESET}")
print(f" {YELLOW}Please replace them with this perfectly formatted block:{RESET}\n")

for line in ideal_block:
if line == "":
print("")
else:
print(f"{GREEN}{line}{RESET}")

print(f"\n {CYAN}-------------------------------------------{RESET}")
return True

return False

def main():
files = get_staged_cpp_files()
if not files:
sys.exit(0)

commit_blocked = False
for f in files:
with open(f, 'r', encoding='utf-8') as file_obj:
lines = file_obj.readlines()

# Run all our quality checks
if check_copyright(f, lines):
commit_blocked = True

if check_includes(f, lines):
commit_blocked = True

if commit_blocked:
print(f"\n{RED}{BOLD} Commit aborted. Please fix the errors above and run 'git add <file>' again.{RESET}")
sys.exit(1)

sys.exit(0)

if __name__ == "__main__":
main()
19 changes: 18 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,21 @@ if(BUILD_TESTING)

include(GoogleTest)
add_subdirectory(tests)
endif()
endif()

# ==============================================================================
# Git Hooks Configuration
# ==============================================================================
# Automatically configure Git to use the custom hooks directory if it exists
if(EXISTS "${CMAKE_SOURCE_DIR}/.githooks")
execute_process(
COMMAND git config core.hooksPath .githooks
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
RESULT_VARIABLE GIT_HOOKS_RESULT
OUTPUT_QUIET
ERROR_QUIET
)
if(GIT_HOOKS_RESULT EQUAL 0)
message(STATUS "Git hooks correctly configured to use .githooks/")
endif()
endif()
3 changes: 2 additions & 1 deletion libs/expressions/include/AlgebraicSimplifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@

#pragma once

#include <vector>
#include <memory>
#include <vector>

#include "CfemExpression.h"

namespace cfem::sym
Expand Down
11 changes: 6 additions & 5 deletions libs/expressions/include/BinaryExpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@

#pragma once

#include "cfemutils.h"
#include "Vector3D.h"
#include "Tensor3D.h"
#include <memory>
#include <span>

#include "CfemExpression.h"
#include "cfemutils.h"
#include "EvalContext.h"
#include <span>
#include <memory>
#include "Tensor3D.h"
#include "Vector3D.h"

namespace cfem::sym

Expand Down
3 changes: 2 additions & 1 deletion libs/expressions/include/CachedExpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@

#pragma once

#include "CfemExpression.h"
#include <memory>
#include <utility>

#include "CfemExpression.h"

namespace cfem::sym {

/**
Expand Down
13 changes: 7 additions & 6 deletions libs/expressions/include/CfemExpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@

#pragma once

#include <memory>
#include <ostream>
#include <set>
#include <span>

#include "cfemutils.h"
#include "EvalContext.h"
#include "Vector3D.h"
#include "Tensor3D.h"
#include "FESpace.h"
#include <set>
#include <span>
#include <memory>
#include <ostream>
#include "Tensor3D.h"
#include "Vector3D.h"


namespace cfem::sym
Expand Down
15 changes: 8 additions & 7 deletions libs/expressions/include/ConstantExpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,17 @@

#pragma once

#include <functional>
#include <memory>
#include <span>
#include <vector>

#include "CfemExpression.h"
#include "cfemutils.h"
#include "Vector3D.h"
#include "Tensor3D.h"
#include "EvalContext.h"
#include "CfemExpression.h"
#include "Tensor3D.h"
#include "Vector3D.h"
#include "ZeroExpression.h"
#include <vector>
#include <span>
#include <memory>
#include <functional>


namespace cfem::sym
Expand Down
11 changes: 6 additions & 5 deletions libs/expressions/include/DivergenceExpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@

#pragma once

#include "cfemutils.h"
#include "Vector3D.h"
#include "CfemExpression.h"
#include "EvalContext.h"
#include <span>
#include <memory>
#include <span>
#include <vector>

#include "CfemExpression.h"
#include "cfemutils.h"
#include "EvalContext.h"
#include "Vector3D.h"

namespace cfem::sym
{
/** *************************************************************************
Expand Down
12 changes: 6 additions & 6 deletions libs/expressions/include/EvalContext_Archive.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@

#pragma once

#include "cfemutils.h"
#include "Vector3D.h"
#include "ReferenceElement.h"
#include "DynamicVector.h"

#include <deque>
#include <vector>
#include <span>
#include <vector>

#include "cfemutils.h"
#include "DynamicVector.h"
#include "ReferenceElement.h"
#include "Vector3D.h"

namespace cfem {

Expand Down
Loading
Loading