本文详细记录在Windows下搭建FreeCAD二次开发环境及创建开发分支的完整流程。
1. 环境准备
| 组件 | 推荐版本/说明 |
|---|---|
| 操作系统 | Windows 11 |
| Visual Studio | Visual Studio Community 2022 |
| VS Code | 1.92.1 |
| Git | 2.34.1 |
| CMake | 3.22.1 |
| Python | 3.11.0 |
| GNU Bison | win_flex_bison-2.5.25 |
| 7-Zip | 官方下载 |
2. 编译安装依赖 LibPack
LibPack 是 FreeCAD 在 Windows 下的依赖集合包。除直接下载外,也可选择自行编译安装以获得更灵活的依赖管理。
2.1 获取 LibPack 源码
前往 FreeCAD/FreeCAD-LibPack 仓库,克隆源码:
bash
git clone https://github.com/FreeCAD/FreeCAD-LibPack.git
2.2 配置与编译 LibPack
阅读仓库内的 README.md,根据说明准备好所需的编译工具(如 Visual Studio、CMake、Python、7-Zip 等)。
进入 LibPack 目录,运行构建脚本(如 build.bat 或 build.ps1,具体以仓库说明为准):
sh
cd FreeCAD-LibPack
build.bat
注1 :自行编译 LibPack 过程较为复杂,可参考笔者博文Windows下编译FreeCAD 1.1 RC2。
注2:笔者已将编译好的资料放到网盘,需要的朋友可联系笔者。
3. 下载 FreeCAD 源码
推荐使用 Git 克隆官方仓库:
sh
git clone --recursive https://github.com/FreeCAD/FreeCAD.git
或使用 Gitee 镜像:
sh
git clone --recursive https://gitee.com/freecad/FreeCAD.git
4. 创建开发分支与 worktree
基于 FreeCAD 进行二次开发,建议使用 Git worktree 管理多分支开发环境。
4.1 创建新分支并添加 worktree
sh
git branch gdcad origin/releases/FreeCAD-1-1
git worktree add gdcad ../FreeCAD.worktrees/GDCAD gdcad
也可以使用下述命令,
sh
git worktree add -b gdcad ../FreeCAD.worktrees/GDCAD origin/releases/FreeCAD-1-1
说明:
-b gdcad创建并切换到新分支gdcad../FreeCAD.worktrees/GDCAD为新工作区目录origin/releases/FreeCAD-1-1为远程分支名
4.2 更新 worktree 中子模块
sh
cd ../FreeCAD.worktrees/GDCAD
git submodule update --init --recursive
如需单独更新某些子模块:
sh
git submodule update --init src/3rdParty/GSL/
git submodule update --init src/3rdParty/OndselSolver/
git submodule update --init src/Mod/AddonManager
git submodule update --init tests/lib
4.3 修改RelWithDbgInfo配置
编辑"cMake\FreeCadMacros.cmake",修改"SET_BIN_DIR",指定RelWithDbgInfo配置的输出,
bash
# Macro to replace all the binary output locations. Takes 2 optional parameters.
# ${ARGVN} is zero based so the 3rd element is ${ARGV2}. When the 3rd element is missing,
# Runtime and Lib directories default to /bin and /lib. When present, the 3rd element
# specifies both Runtime and Lib directories. 4th specifies linux install path.
MACRO(SET_BIN_DIR ProjectName OutputName)
set_target_properties(${ProjectName} PROPERTIES OUTPUT_NAME ${OutputName})
if(${ARGC} GREATER 2)
# VS_IDE (and perhaps others) make Release and Debug subfolders. This removes them.
set_target_properties(${ProjectName} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}${ARGV2})
set_target_properties(${ProjectName} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}${ARGV2})
set_target_properties(${ProjectName} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}${ARGV2})
set_target_properties(${ProjectName} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}${ARGV2})
set_target_properties(${ProjectName} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}${ARGV2})
set_target_properties(${ProjectName} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}${ARGV2})
set_target_properties(${ProjectName} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/lib)
set_target_properties(${ProjectName} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/lib)
else(${ARGC} GREATER 2)
set_target_properties(${ProjectName} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set_target_properties(${ProjectName} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin)
set_target_properties(${ProjectName} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin)
set_target_properties(${ProjectName} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set_target_properties(${ProjectName} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/lib)
set_target_properties(${ProjectName} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/lib)
set_target_properties(${ProjectName} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set_target_properties(${ProjectName} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/bin)
set_target_properties(${ProjectName} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/lib)
endif(${ARGC} GREATER 2)
if(WIN32)
set_target_properties(${ProjectName} PROPERTIES DEBUG_OUTPUT_NAME ${OutputName}_d)
else(WIN32)
# FreeCADBase, SMDS, Driver and MEFISTO2 libs don't depend on parts from CMAKE_INSTALL_LIBDIR
if(NOT ${ProjectName} MATCHES "^(FreeCADBase|SMDS|Driver|MEFISTO2)$")
if(${ARGC} STREQUAL 4)
set_property(TARGET ${ProjectName} APPEND PROPERTY INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/${ARGV3})
elseif(NOT IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR})
set_property(TARGET ${ProjectName} APPEND PROPERTY INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
else()
set_property(TARGET ${ProjectName} APPEND PROPERTY INSTALL_RPATH ${CMAKE_INSTALL_LIBDIR})
endif()
endif()
endif(WIN32)
ENDMACRO(SET_BIN_DIR)
编辑"src\Main\CMakeLists.txt",修改FreeCADGuiPy项目输出名字,防止与FreeCADGui项目冲突,
bash
# SPDX-License-Identifier: LGPL-2.1-or-later
configure_file(freecad.rc.cmake ${CMAKE_CURRENT_BINARY_DIR}/freecad.rc)
configure_file(freecadCmd.rc.cmake ${CMAKE_CURRENT_BINARY_DIR}/freecadCmd.rc)
file(COPY icon.ico DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
######################## FreeCADMain ########################
if(BUILD_GUI)
SET(FreeCAD_SRCS
${CMAKE_CURRENT_BINARY_DIR}/freecad.rc
icon.ico
MainGui.cpp
)
SET(FreeCAD_LIBS
FreeCADGui
)
if(NOT BUILD_DYNAMIC_LINK_PYTHON)
# executables have to be linked against python libraries,
# because extension modules are not.
list(APPEND FreeCAD_LIBS
${Python3_LIBRARIES}
)
endif(NOT BUILD_DYNAMIC_LINK_PYTHON)
add_executable(FreeCADMain WIN32 ${FreeCAD_SRCS})
target_link_libraries(FreeCADMain ${FreeCAD_LIBS})
if (FREECAD_WARN_ERROR)
target_compile_warn_error(FreeCADMain)
endif()
SET_BIN_DIR(FreeCADMain FreeCAD)
if(WIN32)
INSTALL(TARGETS FreeCADMain
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
elseif(APPLE AND NOT BUILD_WITH_CONDA)
INSTALL(TARGETS FreeCADMain
RUNTIME DESTINATION MacOS
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
else()
INSTALL(TARGETS FreeCADMain
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
endif()
endif(BUILD_GUI)
######################## FreeCADMainCmd ########################
SET(FreeCADMainCmd_SRCS
${CMAKE_CURRENT_BINARY_DIR}/freecadCmd.rc
icon.ico
MainCmd.cpp
)
add_executable(FreeCADMainCmd ${FreeCADMainCmd_SRCS})
SET(FreeCADMainCmd_LIBS
FreeCADApp
${QtCore_LIBRARIES}
${QtXml_LIBRARIES}
)
if(NOT BUILD_DYNAMIC_LINK_PYTHON)
# executables have to be linked against python libraries,
# because extension modules are not.
list(APPEND FreeCADMainCmd_LIBS
${Python3_LIBRARIES}
)
endif(NOT BUILD_DYNAMIC_LINK_PYTHON)
target_link_libraries(FreeCADMainCmd
${FreeCADMainCmd_LIBS}
)
if (FREECAD_WARN_ERROR)
target_compile_warn_error(FreeCADMainCmd)
endif()
SET_BIN_DIR(FreeCADMainCmd FreeCADCmd)
if(WIN32)
INSTALL(TARGETS FreeCADMainCmd
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
elseif(APPLE AND NOT BUILD_WITH_CONDA)
INSTALL(TARGETS FreeCADMainCmd
RUNTIME DESTINATION MacOS
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
else()
INSTALL(TARGETS FreeCADMainCmd
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
endif()
######################## FreeCADMainPy ########################
SET(FreeCADMainPy_SRCS
MainPy.cpp
)
add_library(FreeCADMainPy SHARED ${FreeCADMainPy_SRCS})
target_link_libraries(FreeCADMainPy FreeCADApp)
if (FREECAD_WARN_ERROR)
target_compile_warn_error(FreeCADMainPy)
endif()
SET_BIN_DIR(FreeCADMainPy FreeCAD)
SET_PYTHON_PREFIX_SUFFIX(FreeCADMainPy)
if(WIN32)
INSTALL(TARGETS FreeCADMainPy
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
else(WIN32)
INSTALL(TARGETS FreeCADMainPy
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
endif(WIN32)
######################## FreeCADGuiPy ########################
if(BUILD_GUI)
SET(FreeCADGuiPy_SRCS
FreeCADGuiPy.cpp
)
add_library(FreeCADGuiPy SHARED ${FreeCADGuiPy_SRCS})
target_link_libraries(FreeCADGuiPy FreeCADGui)
if (FREECAD_WARN_ERROR)
target_compile_warn_error(FreeCADGuiPy)
endif()
SET_BIN_DIR(FreeCADGuiPy FreeCADGuiPy)
SET_PYTHON_PREFIX_SUFFIX(FreeCADGuiPy)
if(WIN32)
# Name clash with target "FreeCADGui"
# Must be called "FreeCADGuiPy_d" and "FreeCADGuiPy" to work so override default
set_target_properties(FreeCADGuiPy PROPERTIES PDB_NAME_DEBUG "FreeCADGuiPy_d")
set_target_properties(FreeCADGuiPy PROPERTIES PDB_NAME_RELEASE "FreeCADGuiPy")
set_target_properties(FreeCADGuiPy PROPERTIES PDB_NAME_RELWITHDEBINFO "FreeCADGuiPy")
endif(WIN32)
if(WIN32)
INSTALL(TARGETS FreeCADGuiPy
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
else(WIN32)
INSTALL(TARGETS FreeCADGuiPy
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
endif(WIN32)
endif(BUILD_GUI)
5. 配置与编译
5.1 配置相关环境命令
新建".../FreeCAD.worktrees/GDCAD/gdcad-dev.bat",添加以下内容
bat
@ECHO off
@SET Current_DIR=%~dp0
@SET Parent_DIR=%Current_DIR%\..\
:: GDCAD Build
@SET GDCAD_BUILD=%Parent_DIR%BUILD\GDCAD
@SET GDCAD_BUILD_BIN=%GDCAD_BUILD%\bin
@SET GDCAD_BUILD_LIB=%GDCAD_BUILD%\lib
:: LibPack
@SET LIBPACK=%Parent_DIR%\YQ_LibPack-1.1.0-v3.1.1.3-Release
@SET LIBPACK_BIN=%LIBPACK%\bin
@SET LIBPACK_LIB=%LIBPACK%\lib
:: ==============================================================
:: Environment variables for Python
:: the location of the standard Python libraries.
@SET PYTHONHOME=%LIBPACK_BIN%
:: the default search path for module files.
@SET PYTHONPATH=%PYTHONHOME%;%PYTHONHOME%\Scripts;%PYTHONHOME%\Lib;%PYTHONHOME%\Lib\site-packages\shiboken6;%PYTHONHOME%\Lib\site-packages\PySide6
@SET FC_PYTHONHOME=%LIBPACK_BIN%
:: ==============================================================
:: System Enviroment variables
@PATH %GDCAD_BUILD_BIN%;%GDCAD_BUILD_LIB%;%LIBPACK_BIN%;%LIBPACK_LIB%;%LIBPACK%\win64\vc14\bin;%PYTHONPATH%;%PATH%
@CMD
5.2 CMake 配置工程
双击打开".../FreeCAD.worktrees/GDCAD/gdcad-dev.bat",运行
bash
cmake-gui
按下表完成 CMake 配置,
| 配置项 | 示例值/说明 |
|---|---|
| CMAKE_INSTALL_PREFIX | D:/YouQuan/CaeFrameworks/FreeCAD/INSTALL/GDCAD |
| FREECAD_LIBPACK_DIR | D:/YouQuan/CaeFrameworks/FreeCAD/FreeCAD-LibPack/working/LibPack-1.1.0-v3.1.1.3-Release |
| BUILD_DYNAMIC_LINK_PYTHON | OFF |
| BUILD_TEST | OFF |
| ENABLE_DEVELOPER_TESTS | OFF |
5.3 编译
在CMake中点击'Open Project',切换到'RelWithDbgInfo',进行编译。
6. 调试
新建"D:\YouQuan\CaeFrameworks\FreeCAD\BUILD\GDCAD\bin\qt.conf",添加以下内容,
bash
[Paths]
Prefix=D:/YouQuan/CaeFrameworks/FreeCAD/FreeCAD-LibPack/working/LibPack-1.1.0-v3.1.1.3-Release
Documentation=doc
Headers=include
Libraries=lib
Binaries=bin
Plugins=plugins
QmlImports=qml
ArchData=.
Data=.
Translations=translations
Examples=examples
Tests=tests
Settings=.
将"D:\YouQuan\CaeFrameworks\FreeCAD\FreeCAD-LibPack\working\LibPack-1.1.0-v3.1.1.3-Release\win64\vc14\bin"拷贝到"D:\YouQuan\CaeFrameworks\FreeCAD\BUILD\GDCAD\bin"。
7. FAQs
Q: VS 2022 RelWithDbg调试,断点不生效,显示"The breakpoint will not currently be hit",
A: 编辑项目属性,在[C/C++]/[Optimization]中,将"Optimization"属性设为"Disabled(/Od)",重新编译即可。