refactor: re-implement the project, which should be available for both org and com version of OpenFOAM

This commit is contained in:
Zhuo Yang 2024-09-11 21:19:50 +08:00
parent 412142f88e
commit 9b874ad9b9
7 changed files with 168 additions and 164 deletions

View File

@ -1,13 +0,0 @@
### 该问题是怎么引起的?
### 重现步骤
### 报错信息

View File

@ -1,11 +0,0 @@
### 相关的Issue
### 原因(目的、解决的问题等)
### 描述(做了什么,变更了什么)
### 测试用例(新增、改动、可能影响的功能)

View File

@ -1,21 +1,24 @@
# of_cmake_config
[中文](./README.zh_CN.md) | [English](./README.md)
#### Description
This project is used to generate CMakeLists.txt for OpenFOAM project.
#### Introduction
This project is used to generate `CMakeLists.txt` for OpenFOAM projects.
#### Installation
0. Activate OpenFOAM environment:
- Using alias `of2012clang` or `of2012clangdebug`
- source directly: `source $HOME/OpenFOAM/OpenFOAM-v2012/etc/bashrc WM_COMPILER=Clang ...`
1. Get this project: `git clone https://github.com/zhyang-dev/of_cmake_config.git`
#### Installation Guide
0. Activate the required OpenFOAM environment (otherwise the installation will fail).
1. Obtain the project source code: `git clone https://github.com/zhyang-dev/of_cmake_config.git`
2. Install: `cd of_cmake_config && ./install`
#### Instructions
#### Usage Instructions
1. Activate OpenFOAM environment
2. In project root path,
- run `ofCmakeConfig` to generate `CMakeLists.txt`
- or run `occ`, which is a wrapper of `ofCmakeConfig` script, to generate `CMakeLists.txt` and `compile_commands.json`.
0. Activate the OpenFOAM environment.
1. In the project's root directory:
- Run `ofCmakeConfig` to generate `CMakeLists.txt`.
- Or run `occ`, which will execute `ofCmakeConfig` and call `cmake -B build`, eventually generating `compile_commands.json` in the build directory.
![video demo](demo/occ_demo.gif)
#### Testing Environment
- `vscode` + `clangd`
- `vim` + `coc-clangd`
The following demonstrates the second scenario based on the icoFoam case.
![Video Demo](demo/occ_demo.gif)

View File

@ -5,9 +5,7 @@
本项目用于生成OpenFOAM项目的CMakeLists.txt
#### 安装教程
0. 激活OpenFOAM环境:
- 使用别名 `of2012clang` or `of2012clangdebug`
- 直接source: `source $HOME/OpenFOAM/OpenFOAM-v2012/etc/bashrc WM_COMPILER=Clang ...`
0. 激活需要的OpenFOAM环境否则会安装失败
1. 获取该项目源码:`git clone https://github.com/zhyang-dev/of_cmake_config.git`
2. 安装:`cd of_cmake_config && ./install`
@ -15,12 +13,12 @@
0. 激活OpenFOAM环境
1. 在项目根目录下,
- 运行 `ofCmakeConfig`,生成`CMakeLists.txt`
- 或运行`occ`,其包装了`ofCmakeConfig`,除了生成`CMakeLists.txt`还会调用cmake生成`compile_commands.json`。
- 运行 `ofCmakeConfig`,可以生成`CMakeLists.txt`
- 或运行`occ`,它会执行`ofCmakeConfig`,并调用`cmake -B build`最终在build中会生成`compile_commands.json`。
#### 测试环境
- `vscode` + `clangd`
- `vim` + `coc-clangd`
以下基于icoFoam算例演示第二种情况。
![视频演示](demo/occ_demo.gif)

View File

@ -3,12 +3,10 @@
objDir=$WM_PROJECT_DIR/wmake
if [ -d $objDir ]
then
cwd=`pwd`
chmod +x ./ofCmakeConfig
chmod +x ./occ
rm -f $objDir/ofCmakeConfig $objDir/occ
ln -s $cwd/ofCmakeConfig $objDir/
ln -s $cwd/occ $objDir/
cp ofCmakeConfig occ wmakelog2cmakelists.py $objDir/
echo "installed ofCmakeConfig"
else
echo "please active OpenFOAM environment"

View File

@ -1,127 +1,31 @@
#!/bin/bash
Script="${0##*/}" # Need 'Script' for wmakeFunctions messages
scriptsDir="${0%/*}"/scripts # wmake/scripts directory
. "$scriptsDir"/wmakeFunctions # Source wmake functions
printInfo() {
if [ -f "$WM_DIR"/makefiles/info ]
then
make --no-print-directory -f "$WM_DIR"/makefiles/info "$@"
else
echo "OpenFOAM environment not set?" 1>&2
return 1
fi
}
if [ -z "$objectsDir" ]
if [ ! -f "log.wmake" ]
then
objectsDir="$MakeDir/$WM_OPTIONS"
fi
mkdir -p "$objectsDir"
make -s -f "$WM_DIR"/makefiles/files MAKE_DIR="$MakeDir" OBJECTS_DIR="$objectsDir" "$objectsDir"/options
make -s -f "$WM_DIR"/makefiles/files MAKE_DIR="$MakeDir" OBJECTS_DIR="$objectsDir"
s=`make --dry-run -f "$WM_DIR"/makefiles/general MAKE_DIR="$MakeDir" OBJECTS_DIR="$objectsDir" $targetType`
if [ $WM_COMPILE_OPTION = "Debug" ]; then
echo $s
echo "wmake > log.wmake"
wmake $@ >log.wmake 2>&1
fi
link_flags_extra=`echo $s | grep -oP '(?<=Xlinker)(.*?)(?=Make)'`
link_flags_extra="-Xlinker $link_flags_extra"
# Find the Python executable in the system
PYTHON_EXEC=$(which python3)
OF_compile_flags=`printInfo "cxxflags"`
OF_compile_flags="$OF_compile_flags -iquote."
OF_link_flags="$OF_compile_flags $link_flags_extra"
OF_project_name=`cat Make/files | grep EXE | grep -oP '(?<=/).*$'`
OF_typestr="add_executable"
if [ -z $OF_project_name ]
then
OF_project_name=`cat Make/files | grep LIB | grep -oP '(?<=/).*$'`
OF_typestr="add_library"
# Check if python3 is found
if [ -z "$PYTHON_EXEC" ]; then
echo "python3 not found"
exit 1
fi
OF_sources=`cat Make/files | grep -oP '.*\.C'`
if [ $OF_typestr = "add_executable" ]
then
OF_typestr="$OF_typestr(\${project_name} \${OF_sources})"
# Get the Python version
PYTHON_VERSION=$($PYTHON_EXEC -c 'import sys; print(".".join(map(str, sys.version_info[:3])))')
# Split the version number
IFS='.' read -r -a version_parts <<< "$PYTHON_VERSION"
# Compare major and minor versions
if [ "${version_parts[0]}" -gt 3 ] || { [ "${version_parts[0]}" -eq 3 ] && [ "${version_parts[1]}" -ge 6 ]; }; then
script_dir=$(cd "$(dirname "$0")" && pwd)
$PYTHON_EXEC $script_dir/wmakelog2cmakelists.py
else
OF_typestr="$OF_typestr(\${project_name} SHARED \${OF_sources})"
fi
str_inc=`echo $s | sed 's/ /\n/g' | grep -E '^-I' | sort | uniq |grep -oP '(?<=-I).*'`
OF_includeDir_tmp=${str_inc/%lnInclude/}
OF_includeDir=""
for inc in $OF_includeDir_tmp
do
if [ "${inc:0:1}" != "/" ]
then
inc="\${CMAKE_SOURCE_DIR}/$inc"
fi
OF_includeDir="$OF_includeDir $inc"
done
OF_includeDir="$OF_includeDir \${CMAKE_SOURCE_DIR}/lnInclude"
OF_includeDir=`echo $OF_includeDir | sed 's/ /\n/g'`
OF_linkLib=`echo $s | sed 's/ /\n/g' | grep -E '^-l' | sort | uniq |grep -oP '(?<=-l).*'`
OF_linkDir=$WM_PROJECT_DIR/platforms/$WM_OPTIONS/lib
OF_linkDir=`echo "$OF_linkDir $FOAM_USER_LIBBIN" | sed 's/ /\n/g'`
# 写入文件 CMakeLists.txt
cat > CMakeLists.txt <<EOF
cmake_minimum_required(VERSION 3.12)
#=== OF related variables ===
set(project_name $OF_project_name)
set(WM_COMPILE_OPTION $WM_COMPILE_OPTION)
set(OF_compile_flags "$OF_compile_flags")
set(OF_link_flags "$OF_link_flags")
set(OF_testCaseDir \${CMAKE_SOURCE_DIR}/testCase)
set(OF_sources
$OF_sources
)
set(OF_includeDir
$OF_includeDir
)
set(OF_linkLib
$OF_linkLib
)
set(OF_linkDir
$OF_linkDir
)
#=== End ===
project(\${project_name} LANGUAGES CXX C)
set(ENV{LD_LIBRARY_PATH} "\$ENV{LD_LIBRARY_PATH}:\${OF_linkDir}")
if(\${WM_COMPILE_OPTION} STREQUAL "Opt")
set(CMAKE_BUILD_TYPE Release CACHE STRING "Release mode" FORCE)
endif()
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED)
link_directories(
\${OF_linkDir}
)
include_directories(
\${OF_includeDir}
\${CMAKE_SOURCE_DIR}/lnInclude
\${CMAKE_SOURCE_DIR}
)
${OF_typestr}
target_link_libraries(
\${project_name}
PRIVATE
\${OF_linkLib}
)
set_target_properties( \${project_name} PROPERTIES
COMPILE_FLAGS \${OF_compile_flags}
LINK_FLAGS \${OF_link_flags}
WORKING_DIRECTORY \${OF_testCaseDir})
EOF
echo "Python version must be greater than 3.6, current version is: $PYTHON_VERSION"
exit 1
fi

125
wmakelog2cmakelists.py Normal file
View File

@ -0,0 +1,125 @@
import re
def remove_duplicates(seq):
"""
Remove duplicates from the seq list and maintain the original order
"""
seen = set()
return [x for x in seq if not (x in seen or seen.add(x))]
def preprocess_lines(lines):
"""
Merge multiple lines with newline characters
"""
merged_lines = []
current_line = ""
for line in lines:
stripped_line = line.strip()
if stripped_line.endswith('\\'):
current_line += stripped_line[:-1].strip() + ' '
else:
current_line += stripped_line
merged_lines.append(current_line)
current_line = ""
return merged_lines
def parse_wmake_log(log_file):
"""
There may be multiple compilation targets; distinguish between compilation and linking.
"""
with open(log_file, 'r') as file:
lines = file.readlines()
# Preprocess line merging
lines = preprocess_lines(lines)
# Initialize data storage
source_files = set()
compile_options = []
link_options = []
link_libraries = []
link_directories = set()
include_directories = set()
compile_definitions = set()
output_file = ""
cxx_standard = ""
for line in lines:
if 'g++' in line:
# Check for C++ standard settings
cxx_match = re.search(r'-std=c\+\+(\d+)', line)
if cxx_match:
cxx_standard = cxx_match.group(1)
# Determine if it's a compile or link statement
if '-Xlinker' in line or '-Wl,' in line:
# Link statement
output_file = re.search(r'-o\s+(\S+)', line).group(1)
lib_matches = re.findall(r'-l(\S+)', line)
for lib in lib_matches:
if lib not in link_libraries:
link_libraries.append(lib)
link_directories.update(re.findall(r'-L(\S+)', line))
options = re.findall(r'( -\S+)', line)
# Handle -Xlinker options
xlinker_options = re.findall(r'-Xlinker\s+(\S+)', line)
link_options.append(f'-Wl,{",".join(xlinker_options)}')
else:
# Compile statement
source_files.update(re.findall(r'-c\s+(\S+)\s+-o', line))
include_directories.update(re.findall(r'-I(\S+)', line))
compile_definitions.update(re.findall(r'-D(\S+)', line))
options = re.findall(r'(-\S+)', line)
exclude_prefixes = ('-I', '-D', '-o', '-c', '-std=c++')
compile_options.extend(opt for opt in options if not any(
opt.startswith(prefix) for prefix in exclude_prefixes))
# Handle specific include directories
include_directories = {f"${{CMAKE_SOURCE_DIR}}/{dir}" if dir ==
'lnInclude' else dir for dir in include_directories}
include_directories.add("${CMAKE_SOURCE_DIR}")
objfile_split = output_file.split('/')[-1].split('.')
objname = objfile_split[0]
objstr = ''
if len(objfile_split) == 1:
objstr = f'add_executable({objname} {" ".join(source_files)})'
elif objfile_split[1] == 'so':
objstr = f'add_library({objname} SHARED {" ".join(source_files)})'
elif objfile_split[1] == 'a':
objstr = f'add_library({objname} STATIC {" ".join(source_files)})'
# Handle the issue of link and target having the same name
link_libraries = [lib if lib != objname else f'lib{
lib}.so' for lib in link_libraries]
# Create CMakeLists.txt content
cmake_content = f"""cmake_minimum_required(VERSION 3.10)
project({output_file.split('/')[-1].split('.')[0]})
set(CMAKE_CXX_COMPILER g++)
set(CMAKE_CXX_STANDARD {cxx_standard})
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
{objstr}
target_include_directories({objname} PRIVATE {" ".join(include_directories)})
target_compile_definitions({objname} PRIVATE {" ".join(compile_definitions)})
target_compile_options({objname} PRIVATE {" ".join(remove_duplicates(compile_options))})
target_link_directories({objname} PRIVATE {" ".join(remove_duplicates(link_directories))})
target_link_libraries({objname} {" ".join(remove_duplicates(link_libraries))})
target_link_options({objname} PRIVATE {" ".join(link_options)})
"""
return cmake_content
def write_cmake_file(content, output_filename="CMakeLists.txt"):
with open(output_filename, 'w') as file:
file.write(content)
if __name__=="__main__":
# Use functions to parse the log and write CMakeLists.txt
cmake_content = parse_wmake_log("log.wmake")
write_cmake_file(cmake_content)
print("CMakeLists.txt has been generated successfully.")