# c_cpp_project_framework **Repository Path**: liunix61/c_cpp_project_framework ## Basic Information - **Project Name**: c_cpp_project_framework - **Description**: Project manager framework------for c/cc - **Primary Language**: C - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2023-09-13 - **Last Updated**: 2025-06-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README C CPP Project Framework (Template) =================== [English](./README.md) 一个足够 **简单易用** 并且 **可配置的**用于构建 `C/C++` 的模板工程 > 使用 `CMake` 构建,并且支持带`GUI`配置界面的 `Kconfig` 基于此工程,可以快速搭建你的项目构建系统,减少许多不必要的麻烦~ 希望能帮到你,如果帮到了请右上角给颗小星星哦 ~~ 如果有啥不足欢迎提 [issue](https://github.com/Neutree/c_cpp_project_framework/issues/new)。 `(´ε` ʃƪ)♡` 本项目的目标人群: * 正准备写 `SDK` 并且需要自己写构建系统的开发者 * 正准备开始编写一个需要写构建系统的开发者 * 对`CMake`不太了解,但想学习 `CMake` 的开发者 * 想重构代码工程构建系统的,比如因为之前写的构建系统太杂乱 * 想给构建系统中加一个十分好用的配置系统,可以按需求快速增删代码模块的,而且最好带界面配置的 开发者 * 想让项目可以生成多种 `IDE` 支持的工程 ![](assets/image/build.gif) ## 特性 * 语法简单, 无需`Makefile`或者`CMake`知识, 只需基于模板修改变量的值即可非常简单地用起来了 * 基于组件(component)概念的项目结构, 方便搭建层次结构清晰的项目架构 * 组件之间调用只需要一个语句指定依赖即可(比如`list(APPEND ADD_REQUIREMENTS component1)`), 无需设置多余变量比如`INCLUDE 路径`即可在源码中调用依赖的组件的内容 * 使用 `Kconfig` 使项目组件和代码可裁剪配置, 方便项目满足不同的需求 * 方便地引入静态库(`.a`) 和 动态库(`.so`) ( 比如`list(APPEND ADD_STATIC_LIB "lib/libtest.a")`) * 方便地生成静态库(`.a`) 和 动态库(`.so`) (默认生成静态库, 需要组件生成动态库,则使用`register_component(DYNAMIC)`注册模块即可) * 使用 `Python` 脚本作为辅助, 可方便地添加命令和工具, 编译只需要执行简单的命令即可(如`python project.py build` `python project.py menuconfig`) * 方便地作为 `SDK`, 工程实例可以直接放在`SDK`目录下, 也可以单独放在磁盘任何地方, 只需要设置环境变量`MY_SDK_PATH`即可 * 交叉编译友好, 很好地作为嵌入式设备 `SDK` * 生成各种 `IDE` 可以直接使用的工程文件, 可以方便地导入到各种 `IDE` 中使用 * 支持编译成 `WASM`(Web Assembly) * 支持 VSCode + GDB 在线调试(PC 或者嵌入式设备均支持) ## 快速上手 * 克隆代码: ``` git clone https://github.com/Neutree/c_cpp_project_framework --recursive ``` > 参数 `--recursive` 是必须的, 是为了克隆子模块, 否则克隆下来的代码不完整 * 或者基于这个模板仓库创建一个你自己的 github 仓库:
点击 `use this template` 按钮即可
![](assets/image/use_template.png) * 开始编译
有两种方法, 使用`project.py`脚本或者使用原生`CMake`命令 * 使用`project.py`脚本 ``` cd examples/demo1 # python project.py --toolchain /opt/toolchain/bin --toolchain-prefix mips-elf- config python project.py menuconfig python project.py build # python project.py rebuild # 当删减了文件时, 需要使用此命令而不是 build 命令 # 可以使用 --verbose 参数来打印编译时的调试信息,在编译出现问题时十分有用 # python project.py build --verbose python project.py run # 或者 ./build/demo1 python project.py clean python project.py distclean # python project.py clean_conf ``` * 切换工程目录 * 设置工具链路径以及前缀(如果使用`gcc`不需要设置) * 使用命令 `python project.py menuconfig` 来通过终端图形界面配置工程, 这会在 `build/config` 目录生成几个配置文件(`global_config.*`), 我们可以直接在组件(`component`)的`CMakelists.txt` 文件中直接使用(详细看后面的说明), 或者在 `C/CPP`源文件中通过语句 `#include "global_config.h"` 包含配置头文件来使用 * 使用命令 `python project.py build` 来启动构建, 可以使用`python project.py build --verbose`命令来打印编译时的调试信息, 在遇到编译错误时十分有用 * 使用`python project.py clean`来清除构建中间文件, 使用`python project.py distclean`来清楚`menuconfig`命令生成的文件, 这个命令不会清除工具链配置 * 使用`python project.py clean_conf`来清除工具链配置 * 使用原生`CMake`命令 ``` cd examples/demo1 # python project.py --toolchain /opt/toolchain/bin --toolchain-prefix mips-elf- config mkdir build && cd build cmake .. make menuconfig make -j10 ./build/demo1 make clean rm -rf ./* ``` * 切换工程目录 * 设置工具链路径以及前缀(如果使用`gcc`不需要设置) * 建立一个临时目录并且切换当前路径到这个临时目录(`build`) * 使用命令 `cmake ..` 来生成 `Makefile`, 这里 `..` 表示上层目录,即项目的目录 * 使用命令 `make menuconfig` 来通过终端图形界面配置工程, 这会在 `build/config` 目录生成几个配置文件(`global_config.*`), 我们可以直接在组件(`component`)的`CMakelists.txt` 文件中直接使用(详细看后面的说明), 或者在 `C/CPP`源文件中通过语句 `#include "global_config.h"` 包含配置头文件来使用 * 使用命令 `make` 来执行编译链接过程, 或者并行编译: [make -jN](http://www.gnu.org/software/make/manual/make.html#Parallel), 以及通过 `make VERBOSE=1` 命令来打印编译时的调试信息 ## 目录结构 | 目录/文件 | 功能 | | -------------- | -------- | | 根目录 | 本项目的根目录,也是 `SDK` 项目的 `SDK` 目录| | assets | 存放多媒体资源的文件夹,比如图片等,如果不需要可以删除 | | components | 组件(component)都放在这里 | | examples | 工程目录,或者例程目录;在 `SDK` 项目中这个目录是可以和 `SDK` 目录分开放的, 只需要设置环境变量`MY_SDK_PATH`为`SDK`目录路径即可 | | tools | 工具目录比如 `cmake`、`kconfig`、`burn tool` 等等 | | Kconfig | `Kconfig` 的最顶层配置 | ### 1) 组件(component) 所有库均作为组件(component)被放在`components`目录下或者工程目录下,每个组件用一个目录,这个目录就是这个组件的名字, 为了使工程看起来更简洁,不对组件进行嵌套,所有组件都是一个层级,组件之间的关系依靠依赖关系(requirements)来维持 所有源文件都必须在某个组件内,每个工程必须包含一个叫 `main` 的组件(即`examples/demo1/main` 目录),每个组件包含文件如下: * `CMakeLists.txt`: 必须有,声明组件源文件以及依赖的组件,并且调用注册函数注册自己,详细可以参考`components/component1`和`components/component2`下`CMakeLists.txt`的写法 * `Kconfig`: 可选,包含了本组件的配置选项, 在本组件或者其它依赖了本组件的组件的`CMakeLists.txt`中都可以在加一个`CONFIG_`前缀后使用这些配置项,比如在`components/component2`中,`Kconfig`中有`COMPONENT2_ENABLED` 选项,我们在它的`CMakeLists.txt`中就使用了这个变量`if(CONFIG_COMPONENT2_ENABLED)`来判断如果用户配置不用这个组件就不注册这个组件 ### 2) 工程目录 工程目录在`examples`目录下,当然,这个目录的名字是可以随意根据实际需求修改的,下面可以包含多个实际的工程目录,需要编译那个工程时切换到对应的目录就可以编译。上面也说了,每个工程目录下必须有一个 `main` 组件, 当然也可以放很多自定义的组件。 可以参考`examples/demo1`工程目录。 工程目录下文件: * `CMakeLists.txt`: 工程属性文件,必须先包含`include(${SDK_PATH}/tools/cmake/compile.cmake)`,然后用`project`函数声明工程名称,比如`project(demo1)`,当然还可以编写其它的条件或者变量等,使用`CMake`语法, 参考`examples/demo1/CMakeLists.txt`的写法 * `config_defaults.mk`: 工程默认配置文件,执行`cmake`构建时会从这里加载默认配置,配置的格式是`Makefile`的格式,可以先使用终端界面配置(`make menuconfig`)生成配置文件复制过来,生成的配置文件在`build/config/global_config.mk`。 > 注意:每次修改`config_defaults.mk` 后需要删除`build`目录下的文件(或者只删除`build/config/global_config.mk`文件)重新生成,因为当前构建系统会优先使用`build`目录下已经存在的配置文件 * `project.py`: 工具脚本调用入口, 使用`python project.py menuconfig` `python project.py build` 等命令来开始构建 如何将工程目录放在磁盘的任何地方: * 将`CMakeLists.txt`和 `project.py` 中的 `MY_SDK_PATH` 改成你喜欢的环境变量名称, 然后在终端中设置这个环境变量的值为`SDK`的路径, 即可将这个工程目录放到任何地方也可以编译了 ## SDK 和 工程目录分开存放 通常情况下,只需要按照自己的需求,修改`example`目录的名字,比如改成`projects`,或者在工程根目录重新创建一个目录也是可以的,比如`projects/hello_world`,然后拷贝`examples/demo1`中的内容来新建一个新的工程 另外,工程目录和 SDK 目录也可以分开存放,这通常适用于开源项目,一份SDK,用户基于这份 SDK 开发,这样更利于源码传播,用户不需要拷贝一份 SDK, 只需要指定使用的 SDK 版本(git 提交号)。 要做到,只需要: * 下载 `SDK` 放到某个目录,比如 `/home/neucrack/my_SDK` ``` git clone https://github.com/Neutree/c_cpp_project_framework --recursive ``` 注意这里用了`--recursive`参数, 因为工程中使用了子模块,子模块的好处是各个工程分开管理,比如这里用了`Kconfiglib`作为子模块,提供`menuconfig`带界面的功能配置功能 **注意如果不更新子模块,将无法通过编译!!!** 如果克隆的时候忘记加这个参数了,也可以再使用下面这个命令来更新子模块: ``` git submodule update --init --recursive ``` 另外,当远程仓库更新了,用户也需要使用以下命令来更新代码(即同时更新子模块代码): ```shell git pull --recursive ``` 或者: ``` git pull git submodule update --init --recursive ``` 当然,你也可以选择删除 `.git` 目录,然后重新创建一个没有子模块的 git 仓库~ * 然后在终端导出变量 `export MY_SDK_PATH=/home/neucrack/my_SDK`, 可以放到 `~/.bashrc`或者`~/.zshrc`文件中,这样每次终端启动都会自动添加这个变量了 * 然后在任意地方建立工程, 比如拷贝`example/demo1`整个文件夹中的所有文件到`/home/neucrack/temp/my_projects/demo1` * 然后清除之前的构建缓存(如果有的话,没有就忽略) ``` python3 project.py distclean ``` * 然后配置和构建即可 ``` python3 project.py menuconfig python3 project.py build ``` ## 设置自定义组件库路径 一般来说通用的组件放在 `SDK 目录 -> components 目录`下, 而工程特有的组件放在 `工程目录`下。 除此之外,用户还可以自定义自己的通用组件的存放位置,通过在系统环境变量设置 `CUSTOM_COMPONENTS_PATH` 来指定自定义组件的存放位置,比如: Linux 下: ``` export CUSTOM_COMPONENTS_PATH=/home/neucrack/my_components ``` Windows 直接在环境变量界面中添加`CUSTOM_COMPONENTS_PATH`变量即可。 > `CUSTOM_COMPONENTS_PATH`这个名称可以在工程的`project.py`和`CMakeLists.txt`中根据你的项目名称或者喜好修改。 然后就能在工程的组件里面直接通过 `list(APPEND ADD_REQUIREMENTS 组件名)` 来引用了。 ## 调试 (Debug) 版本和发布 (Release) 版本 默认都是以 debug 版本编译,如果要发布版本,可以使用以下命令: ```shell python project.py distclean python project.py build --release ``` 此时构建的二进制文件就是 release 版本,编译脚本做了几个动作: * 设置 CMake 环境变量 `CMAKE_BUILD_TYPE` 为 `MinSizeRel`(默认是 `Debug`) * 在生成的头文件`global_config.h`中添加了 `#define RELEASE 1`(默认会加`#define DEBUG 1`) * 在编译时自动添加了`RELEASE=1`的宏定义,所以代码其实不用引入`global_config.h`也可以通过`RELEASE`和`DEBUG`宏定义判断当前是 release 版本还是 debug 版本 ## 更换工程生成器 有时你可能需要更快的构建速度,或者需要生成一个工程给 IDE 使用,比如 Visual Studio, 可以通过更改工程生成器实现, 默认的生成器是 `Unix Makefiles`。 有多种生成器选择,比如 `Ninja`, `Visual Studio`, `Xcode`, `Eclipse`, `Unix Makefiles` 等等。 执行命令 `cmake --help` 可以查看生成器选择,不同的系统支持不同的生成器。 比如 Linux 下: ``` Generators The following generators are available on this platform (* marks default): Green Hills MULTI = Generates Green Hills MULTI files (experimental, work-in-progress). * Unix Makefiles = Generates standard UNIX makefiles. Ninja = Generates build.ninja files. Ninja Multi-Config = Generates build-.ninja files. Watcom WMake = Generates Watcom WMake makefiles. CodeBlocks - Ninja = Generates CodeBlocks project files. CodeBlocks - Unix Makefiles = Generates CodeBlocks project files. CodeLite - Ninja = Generates CodeLite project files. CodeLite - Unix Makefiles = Generates CodeLite project files. Eclipse CDT4 - Ninja = Generates Eclipse CDT 4.0 project files. Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files. Kate - Ninja = Generates Kate project files. Kate - Unix Makefiles = Generates Kate project files. Sublime Text 2 - Ninja = Generates Sublime Text 2 project files. Sublime Text 2 - Unix Makefiles = Generates Sublime Text 2 project files. ``` 通过 `config` 命令来更改生成器 ``` # 首先清理干净之前的构建缓存(就是删除 build 目录) python project.py distclean python project.py -G Ninja config # python project.py -G "Eclipse CDT4 - Ninja" config python project.py build ``` ## 编译成 WASM 根据 [emscripten-core/emsdk](https://github.com/emscripten-core/emsdk) 先安装工具链 ``` git clone https://github.com/emscripten-core/emsdk.git ./emsdk install latest ./emsdk activate latest ``` 只需要 ``` python project.py distclean python project.py --toolchain $EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake config python project.py build ``` 在 `build` 目录就会出现`demo1.html`, `demo1.js` 和 `demo1.wasm`文件了,直接运行以下命令就会在浏览器执行结果并显示结果了 ``` emrun demo1.html ``` 或者也可以直接使用`node`即可运行 ``` node demo1.js ``` ## 增加新的命令 比如默认使用 `python project.py run` 命令会调用`tools/cmds/run.py`脚本来执行构建出来的可执行文件。 如果你需要给你的 SDK 增加命令,只需要创建一个新的文件,参考[tools/cmds/run.py](./tools/cmds/run.py)文件的写法即可 ## 在线调试 ### VSCode + GDB 在线调试 这里以 PC 为 Linux 系统为例: * 添加 c_cpp_project_framework(第一次试用推荐这样) 或者 工程目录到 VSCode 工作区 * 拷贝 [./assets/vscode_local_debug/.vscode](./assets/vscode_local_debug/.vscode) 目录到上一步的工作目录下 * 根据`.vscode`是在 c_cpp_project_framework 还是在工程目录下,修改`.vscode/launch.json`中的`cwd`字段 * 按键盘 `F5` 即可开始调试 > windows 也类似,修改`.vscode`里面的相关命令和路径即可 ### VSCode + gdbserver 在嵌入式设备(/远程设备,带 Linux 系统)调试 这里以 PC 为 Linux 系统为例: * 先保证远程设备有`gdbserver`这个程序,以及 PC 有`gdb-multiarch`这个程序 * 将 [./assets/vscode_remote_debug/.vscode](./assets/vscode_remote_debug/.vscode) 目录拷贝到工程目录下 * 编辑 `launch.json` 和 `build_run_gdbserver.sh` 文件,修改里面的路径和命令,以及用户名等。 > 建议先将 PC 的 ssh key 加入到远程设备的 `~/.ssh/authorized_keys` 文件中,这样就不需要输入密码了。 * 每次调试需要执行 `build_run_gdbserver.sh` 脚本,然后在 VSCode 中按 `F5` 即可开始调试 > 脚本会编译工程,然后拷贝可执行文件到远程设备,并且启动 `gdbserver`。 > 按 F5 启动调试时, VSCode 使用 GDB 连接到远程设备的`gdbserver`以调试。 ## 开源许可 **MIT**: 详情看 [LICENSE](./LICENSE) 文件 ## 此工程使用到的开源项目 * [Kconfiglib](https://github.com/ulfalizer/Kconfiglib): `Kconfig` `Python` 实现 ## 使用了此框架的仓库 * [Maix-Speech](https://github.com/sipeed/Maix-Speech): 专为嵌入式环境设计的离线语音库 * [MaixPy](https://github.com/sipeed/MaixPy/): 让针对`AIOT`应用的芯片`K210`用上`Micropython` * [libmaix](https://github.com/sipeed/libmaix): 支持多嵌入式平台的带硬件加速的 AI 模型运行库 * [MF1_SDK](https://github.com/sipeed/MF1_SDK): `MF1` AI 模组(开发板)的 SDK ## 相关类似参考项目 * [ESP_IDF](https://github.com/espressif/esp-idf): `ESP32` 的 `SDK`, 写得挺不错 * [RT-Thread](https://github.com/RT-Thread/rt-thread):不是用的 `CMake`, 但是也是使用了组件的概念