├── .github └── workflows │ └── test_build.yml ├── .gitignore ├── .gitmodules ├── Kconfig ├── LICENSE ├── README.md ├── README_ZH.md ├── assets ├── image │ ├── build.gif │ └── use_template.png ├── vscode_local_debug │ └── .vscode │ │ ├── launch.json │ │ └── tasks.json └── vscode_remote_debug │ └── .vscode │ ├── build_run_gdbserver.sh │ └── launch.json ├── components ├── component1 │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Kconfig │ ├── include │ │ └── lib1.h │ ├── lib │ │ └── libtest.a │ └── src │ │ └── lib1.c ├── component2 │ ├── CMakeLists.txt │ ├── Kconfig │ ├── include │ │ └── lib2.h │ ├── include_private │ │ └── lib2_private.h │ └── src │ │ └── lib2.c └── component3 │ ├── CMakeLists.txt │ ├── Kconfig │ ├── include │ └── lib3.h │ ├── include_private │ └── lib3_private.h │ └── src │ └── lib3.c ├── examples └── demo1 │ ├── .gitignore │ ├── CMakeLists.txt │ ├── compile │ └── priority.conf │ ├── config_defaults.mk │ ├── main │ ├── CMakeLists.txt │ ├── Kconfig │ ├── include │ │ └── test.h │ └── src │ │ ├── main.c │ │ ├── test.c │ │ └── test2.c │ └── project.py └── tools ├── cmake ├── compile.cmake ├── compile_flags.cmake ├── gen_binary.cmake ├── project.py ├── sort_components.py ├── tools.cmake └── utils.py ├── cmds ├── flash.py └── run.py └── kconfig ├── genconfig.py └── update_build_info.py /.github/workflows/test_build.yml: -------------------------------------------------------------------------------- 1 | name: test build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | strategy: 10 | max-parallel: 7 11 | matrix: 12 | python-version: ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] 13 | cmake-version: ["3.7.2"] 14 | 15 | steps: 16 | - uses: actions/checkout@master 17 | - name: Get submodules 18 | run: | 19 | git submodule update --init --recursive 20 | - name: Set up Python ${{ matrix.python-version }} 21 | uses: actions/setup-python@v1 22 | with: 23 | version: ${{ matrix.python-version }} 24 | - name: Install dependencies 25 | run: | 26 | python -m pip install --upgrade pip 27 | - name: Test build 28 | run: | 29 | cd examples/demo1 30 | python project.py build 31 | python project.py rebuild 32 | ls -al build/demo1 | awk '{print "program file size: " $5 "B"}' 33 | python project.py run 34 | python project.py clean 35 | python project.py distclean 36 | python project.py build --release 37 | ls -al build/demo1 | awk '{print "program file size: " $5 "B"}' 38 | python project.py distclean 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | build 55 | .vscode 56 | .config 57 | .config.old 58 | .config.mk 59 | !assets/vscode_*_debug/.vscode 60 | 61 | # python 62 | *.pyc 63 | __pycache__ 64 | 65 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tools/kconfig/Kconfiglib"] 2 | path = tools/kconfig/Kconfiglib 3 | url = https://github.com/ulfalizer/Kconfiglib.git 4 | -------------------------------------------------------------------------------- /Kconfig: -------------------------------------------------------------------------------- 1 | 2 | mainmenu "C/CPP CMake project framework Kconfig configuration" 3 | 4 | menu "Toolchain configuration" 5 | config TOOLCHAIN_PATH 6 | string "toolchain path" 7 | default "" 8 | 9 | config TOOLCHAIN_PREFIX 10 | string "toolchain prefix" 11 | default "" 12 | endmenu 13 | 14 | menu "SDK Components Configuration" 15 | osource "${SDK_PATH}/components/*/Kconfig" 16 | osource "${CUSTOM_COMPONENTS_PATH}/*/Kconfig" 17 | endmenu 18 | menu "Project Components Configuration" 19 | osource "${PROJECT_PATH}/../components/*/Kconfig" 20 | osource "${PROJECT_PATH}/*/Kconfig" 21 | osource "${PROJECT_PATH}/components/*/Kconfig" 22 | endmenu 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2023 Neucrack(CZD666666@gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | C CPP Project Framework (Template) 2 | =================== 3 | 4 | [中文](./README_ZH.md) 5 | 6 | **Simple** and **configurable** `C/C++` project/SDK template 7 | > `CMake` build system and support Kconfig with `GUI` configuration 8 | 9 | Based on this project, you can quickly build your project construction system and reduce many unnecessary troubles~ 10 | 11 | If it helps you, please give a little star in the upper right corner~~ If needs improvement, just create an [issue](https://github.com/Neutree/c_cpp_project_framework/issues/new). `(´ε` ʃƪ)♡` 12 | 13 | 14 | The target developer of this project: 15 | 16 | * Developers who are preparing to write `SDK` and need to write their own build system 17 | * Preparing to start writing a project who needs to write a build system 18 | * Who don't know much about `CMake`, but want to learn `CMake` 19 | * Who want to refactor the code engineering build system, maybe because of th bad old messy build system 20 | * Who want to add a very useful configuration system to the build system, you can quickly add and delete code modules as needed, and it is best to have an GUI configuration. 21 | * Who want the project to generate a variety of `IDE` supported projects. 22 | 23 | 24 | ![](assets/image/build.gif) 25 | 26 | 27 | ## Features 28 | 29 | * The syntax is simple, no knowledge of `Makefile` or `CMake` is required, and it is very simple to use only by modifying the value of the variable based on the template 30 | * Project structure based on the concept of component, which is convenient for building a project structure with a clear hierarchical structure 31 | * Calling between components only requires one statement to specify the dependencies (such as `list(APPEND ADD_REQUIREMENTS component1)`), without setting extra variables such as `INCLUDE path` to call the content of the dependent component in the source code 32 | * Use `Kconfig` to enable project components and code to be tailored and configured to facilitate the project to meet different needs 33 | * Conveniently introduce static libraries (`.a`) and dynamic libraries (`.so`) (such as `list(APPEND ADD_STATIC_LIB "lib/libtest.a")`) 34 | * Conveniently generate static libraries (`.a`) and dynamic libraries (`.so`) (by default, static libraries are generated. If you need components to generate dynamic libraries, you can use `register_component(DYNAMIC)` to register the module) 35 | * Using the `Python` script as an aid, you can easily add commands and tools, and you only need to execute simple commands to compile (such as `python project.py build` `python project.py menuconfig`) 36 | * Conveniently used as `SDK`, the project example can be placed directly in the `SDK` directory, or it can be placed anywhere on the disk separately, just set the environment variable `MY_SDK_PATH` 37 | * Cross-compilation friendly, well as an embedded device `SDK` 38 | * Generate a variety of `IDE` supported projects, then you can directly import to IDE as a project 39 | * Support compile to `WASM`(Web Assembly) 40 | 41 | ## Get Started 42 | 43 | * Clone code by: 44 | ``` 45 | git clone https://github.com/Neutree/c_cpp_project_framework --recursive 46 | ``` 47 | > Arg `--recursive` is needed to clone all submodule, or code is not complete 48 | 49 | * Or create your github repository based on this template:
You can create your github repository with this tempalte by click `use this template` button
![](assets/image/use_template.png) 50 | 51 | * Start compile
there's two way, use `project.py` script or use original CMake command 52 | * With project.py(recommend) 53 | ``` 54 | cd examples/demo1 55 | # python project.py --toolchain /opt/toolchain/bin --toolchain-prefix mips-elf- config 56 | python project.py menuconfig 57 | python project.py build 58 | # python project.py rebuild # when you add/remove source files, should use this command instead of build 59 | # you can use --verbose arg to see more compile info, this is useful when error occurs 60 | # python project.py build --verbose 61 | python project.py run # or ./build/demo1 62 | python project.py clean 63 | python project.py distclean 64 | # python project.py clean_conf 65 | ``` 66 | * Change dir to project directory 67 | * Set toolchain path(don't need set if use `gcc`) 68 | * Config project by command `python project.py menuconfig`, it will generate `global_config` files at `build/config` directory, so we can use it in component's `CMakelists.txt` directly, or in `C/CPP` source files by `#include "global_config.h"` 69 | * Build project by command `python project.py build`, or output verbose build info with command `python project.py build --verbose` 70 | * Clean build by `python project.py clean`, clean config generated by `menuconfig` by `python project.py distclean`, this command will not clean toolchain config 71 | * Clean toolchain config by `python project.py clean_conf` 72 | * With original CMake command 73 | ``` 74 | cd examples/demo1 75 | # python project.py --toolchain /opt/toolchain/bin --toolchain-prefix mips-elf- config 76 | mkdir build && cd build 77 | cmake .. 78 | make menuconfig 79 | make -j10 80 | ./build/demo1 81 | make clean 82 | rm -rf ./* 83 | ``` 84 | * Change dir to project directory 85 | * Set toolchain path(don't need set if use `gcc`) 86 | * Make a temporary directory and switch the current path to this directory(`build`) 87 | * Generate makefile by command `cmake ..`, `..` means the project directory 88 | * Config project by command `make menuconfig`, it will generate `global_config` files at `build/config` directory, so we can use it in component's `CMakelists.txt` directly, or in `C/CPP` source files by `#include "global_config.h"` 89 | * Build project by command `make`, or parallel build with [make -jN](http://www.gnu.org/software/make/manual/make.html#Parallel), and output verbose build info with command `make VERBOSE=1` 90 | 91 | 92 | 93 | ## Structure 94 | 95 | | directory/file | function | 96 | | -------------- | -------- | 97 | | root directory | root directory of this project, also `SDK` projects' `SDK` directory | 98 | | assets | store assets like image etc. you can delete it if not use it | 99 | | components | as a component/lib | 100 | | examples | project dir or demo dir; `SDK` projects' example/project dir, this directory can be separated from the `SDK` directory, just set environment`MY_SDK_PATH` to `SDK` directory's path. | 101 | | tools | tools like `cmake`、`kconfig`、`burn tool` etc. | 102 | | Kconfig | root `Kconfig` configuration | 103 | 104 | 105 | ### 1) Component 106 | 107 | All libraries are placed as components in the `components` directory or under the project directory. Each component uses a directory. This directory is the name of the component. In order to make the project look more concise, the components are not nested. All components are a hierarchy, and the relationships between components depend on dependencies to maintain 108 | 109 | All source files must be in a component. Each project must contain a component called `main` (ie `examples/demo1/main` directory). Each component contains the following files: 110 | 111 | * `CMakeLists.txt`: Must exist, declare the component source file and the dependent component, and call the registration function to register itself. For details, please refer to `CMakeLists.txt` of `components/component1` and `components/component2`. 112 | 113 | * `Kconfig`: Optional, contains configuration options for this component. In this component or other `CMakeLists.txt` that depends on the component of this component, you can use these configuration items after adding a `CONFIG_` prefix. e.g. In `components/component2`, there is a `COMPONENT2_ENABLED` option in `Kconfig`. We use this variable `if(CONFIG_COMPONENT2_ENABLED)` in its `CMakeLists.txt` to determine if the user configuration want to register this component or not. 114 | 115 | ### 2) Project Directory 116 | 117 | The project directory is in the `examples` directory. Of course, the name of this directory can be modified according to actual needs. The following can contain multiple actual project directories. You can compile when you need to compile the project and switch to the corresponding directory. As mentioned above, there must be a `main` component in each project directory. Of course, you can also put a lot of custom components. More refer to the `examples/demo1` project directory. 118 | 119 | Files under the project directory: 120 | 121 | * `CMakeLists.txt`: must exist, project properties file, you must first include `include(${SDK_PATH}/tools/cmake/compile.cmake)`, then use the `project` function to declare project name, such as `project(demo1)`, Of course, you can also write other conditions or variables, etc., using the `CMake` syntax, refer to the `examples/demo1/CMakeLists.txt` 122 | 123 | * `config_defaults.mk`: Optional, project default configuration file, the default configuration will be loaded when `cmake` execute. The format of the configuration is `Makefile`. You can use the terminal GUI configuration (`make menuconfig`) to generate the configuration file, the generated configuration file is in `build/config/global_config.mk`, then copy to `config_defaults.mk`. 124 | > Note: After modifying `config_defaults.mk`, you need to delete the files in the `build` directory (or just delete the `mk` file in the `build/config` directory) to regenerate, because the current build system will use the existing configuration file (`build/config/global_config.mk`) 125 | 126 | * `project.py`: tool script call entry, use `python project.py menuconfig` `python project.py build` and other commands to start building 127 | 128 | How to put the project directory anywhere on the disk: 129 | 130 | * Change the `MY_SDK_PATH` in `CMakeLists.txt` and `project.py` to the name of the environment variable you like, and then set the value of this environment variable in the terminal to the path of the `SDK`, you can change the project directory It can be compiled anywhere 131 | 132 | 133 | ## Store SDK and project directory separately 134 | 135 | Normally, you only need to modify the name of the `example` directory according to your needs, such as changing it to `projects`, or creating a new directory in the project root directory, such as `projects/hello_world`, and copy files in the `examples/demo1`'s content to start a project 136 | 137 | In addition, the project directory and the SDK directory can also be stored separately. This is especially used for open source projects, a copy of SDK, users develop based on this SDK, which is more conducive to the spread of routines, users do not need to copy a copy of the SDK, just specify the use SDK version (git commit number) 138 | To do this, only need: 139 | 140 | * Download `SDK` and put it in a directory, such as `/home/neucrack/my_SDK` 141 | 142 | ``` 143 | git clone https://github.com/Neutree/c_cpp_project_framework --recursive 144 | ``` 145 | Note that the `--recursive` parameter is used here, because sub-modules are used in the project. The advantage of sub-modules is that each project is managed separately. For example, `Kconfiglib` is used as a sub-module to provide `menuconfig` with interface function configuration 146 | 147 | **If you did't update submodule, the compile will error!!!!** 148 | 149 | If you forget to add this parameter when cloning, you can also use the following command to update the submodule: 150 | ``` 151 | git submodule update --init --recursive 152 | ``` 153 | In addition, when the remote repository is updated, the user also needs to use the following command to update the code (ie update the submodule code at the same time): 154 | ```shell 155 | git pull --recursive 156 | ``` 157 | or: 158 | ``` 159 | git pull 160 | git submodule update --init --recursive 161 | ``` 162 | 163 | Of course, you can also just delete the `.git` directory, and start a git repository with no submodule~~~ 164 | 165 | 166 | * Then export the variable `export MY_SDK_PATH=/home/neucrack/my_SDK` in the terminal, which can be placed in the `~/.bashrc` or `~/.zshrc` file, so that this variable will be automatically added every time the terminal is started 167 | * Then create a project anywhere, such as copy the entire content of the folder of `example/demo1` to `/home/neucrack/temp/my_projects/demo1` 168 | * Then clear the previous build cache (if there is one, ignore it if there is none) 169 | ``` 170 | python3 project.py distclean 171 | ``` 172 | * Then configure and build 173 | ``` 174 | python3 project.py menuconfig 175 | python3 project.py build 176 | ``` 177 | 178 | ## Custom components path 179 | 180 | Generally, the common components are placed in the `SDK directory -> components directory`, and the project-specific components are placed in the `project directory`. 181 | In addition, users can also customize the storage location of their common components by setting the system environment variable `CUSTOM_COMPONENTS_PATH`, for example: 182 | Linux: 183 | ``` 184 | export CUSTOM_COMPONENTS_PATH=/home/neucrack/my_components 185 | ``` 186 | Windows just add `CUSTOM_COMPONENTS_PATH` variable in the environment variable interface. 187 | > The name `CUSTOM_COMPONENTS_PATH` can be modified according to your project name or preference in the `project.py` and `CMakeLists.txt` of the project. 188 | 189 | Then you can directly use `list(APPEND ADD_REQUIREMENTS component_name)` to reference it in the project component. 190 | 191 | ## Debug and Release version 192 | 193 | By default, it is compiled in debug version. If you want to release version, you can use the following command: 194 | ```shell 195 | python project.py distclean 196 | python project.py build --release 197 | ``` 198 | 199 | Then the binary file built is the release version, and the compilation script does a few actions: 200 | * Set the CMake environment variable `CMAKE_BUILD_TYPE` to `MinSizeRel` (default is `Debug`) 201 | * Add `#define RELEASE 1` to the generated header file `global_config.h` (default will add `#define DEBUG 1`) 202 | * Automatically add the macro definition `RELEASE=1` when compiling, so the code actually does not need to import `global_config.h` can also through `RELEASE` and `DEBUG` macro definition to determine whether the current is release version or debug version 203 | 204 | ## Change project generator 205 | 206 | Sometimes you want to faster build speed or generate project for some IDE like Visual Studio, 207 | you can change generator to achieve this, default generator is `Unix Makefiles`. 208 | 209 | There are many generator choices, such as `Ninja`, `Visual Studio`, `Xcode`, `Eclipse`, `Unix Makefiles` etc. 210 | Execute command `cmake --help` to see the generator choices, different system support different generators. 211 | Linux for example: 212 | ``` 213 | Generators 214 | 215 | The following generators are available on this platform (* marks default): 216 | Green Hills MULTI = Generates Green Hills MULTI files 217 | (experimental, work-in-progress). 218 | * Unix Makefiles = Generates standard UNIX makefiles. 219 | Ninja = Generates build.ninja files. 220 | Ninja Multi-Config = Generates build-.ninja files. 221 | Watcom WMake = Generates Watcom WMake makefiles. 222 | CodeBlocks - Ninja = Generates CodeBlocks project files. 223 | CodeBlocks - Unix Makefiles = Generates CodeBlocks project files. 224 | CodeLite - Ninja = Generates CodeLite project files. 225 | CodeLite - Unix Makefiles = Generates CodeLite project files. 226 | Eclipse CDT4 - Ninja = Generates Eclipse CDT 4.0 project files. 227 | Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files. 228 | Kate - Ninja = Generates Kate project files. 229 | Kate - Unix Makefiles = Generates Kate project files. 230 | Sublime Text 2 - Ninja = Generates Sublime Text 2 project files. 231 | Sublime Text 2 - Unix Makefiles 232 | = Generates Sublime Text 2 project files. 233 | ``` 234 | 235 | You can change it by `config` command 236 | ``` 237 | # clean all build files first(remove build dir) 238 | python project.py distclean 239 | 240 | python project.py -G Ninja config 241 | # python project.py -G "Eclipse CDT4 - Ninja" config 242 | 243 | python project.py build 244 | ``` 245 | 246 | ## Compile to WASM 247 | 248 | Install toolchain first according to [emscripten-core/emsdk](https://github.com/emscripten-core/emsdk) 249 | ``` 250 | git clone https://github.com/emscripten-core/emsdk.git 251 | ./emsdk install latest 252 | ./emsdk activate latest 253 | ``` 254 | 255 | Just only set toolchain 256 | ``` 257 | python project.py distclean 258 | python project.py --toolchain $EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake config 259 | python project.py build 260 | ``` 261 | 262 | Then you will find `demo1.html`, `demo1.js` and `demo1.wasm` files in `build` directory, run command below you will see result in browser: 263 | ``` 264 | emrun demo1.html 265 | ``` 266 | 267 | Or just run by `node` 268 | ``` 269 | node demo1.js 270 | ``` 271 | 272 | ## Add command 273 | 274 | By default we can use `python project.py run` to call [tools/cmds/run.py](./tools/run.py) file, and execute the binary file. 275 | If you want to add commands for your SDK, just create new `py` file in tools directory, write a script and content refer to [tools/cmds/run.py](./tools/cmds/run.py). 276 | 277 | ## Online Debugging 278 | 279 | ### VSCode + GDB Online Debugging 280 | 281 | Here take PC with Linux system as an example: 282 | 283 | * Add `c_cpp_project_framework` (recommended for the first trial) or project directory to VSCode workspace 284 | * Copy the [./assets/vscode_local_debug/.vscode](./assets/vscode_local_debug/.vscode) directory to the working directory of the previous step 285 | * Edit the `cwd` field in `.vscode/launch.json` according to whether `.vscode` is under `c_cpp_project_framework` or under the project directory 286 | * Press F5 on the keyboard to start debugging 287 | > Windows is similar, just modify the relevant commands and paths in `.vscode` 288 | 289 | ### VSCode + gdbserver Debugging on Embedded Device (/Remote Device with Linux System) 290 | 291 | Here take PC with Linux system as an example: 292 | 293 | * Firstly, Ensure that the remote device has the `gdbserver` program, and the PC has the `gdb-multiarch` program 294 | * Copy the [./assets/vscode_remote_debug/.vscode](./assets/vscode_remote_debug/.vscode) directory to the project directory 295 | * Edit the `launch.json` and `build_run_gdbserver.sh` files, modify the paths and commands inside, as well as the username, etc. 296 | > It is recommended to add the PC's ssh key to the `~/.ssh/authorized_keys` file of the remote device first, so you don't need to enter a password. 297 | * The `build_run_gdbserver.sh` script needs to be executed every time you debug, and then press F5 in VSCode to start debugging 298 | > The script will compile the project, then copy the executable file to the remote device, and start `gdbserver`. 299 | > Press F5 to start debugging, VSCode uses GDB to connect to `gdbserver` on the remote device for debugging. 300 | 301 | 302 | ## License 303 | 304 | **MIT**, see [LICENSE](./LICENSE) 305 | 306 | 307 | ## Open source projects used by this project 308 | 309 | * [Kconfiglib](https://github.com/ulfalizer/Kconfiglib): `Kconfig`'s `Python` implementation 310 | 311 | ## Repos used this framwork 312 | 313 | * [Maix-Speech](https://github.com/sipeed/Maix-Speech): AI speech recognization lib for embedded devices 314 | * [MaixPy](https://github.com/sipeed/MaixPy/): `Micropython` port for `AIOT` chip `K210` 315 | * [libmaix](https://github.com/sipeed/libmaix): A lib for embeded AI model running with hardware accelaration 316 | * [MF1_SDK](https://github.com/sipeed/MF1_SDK): SDK for `MF1` AI module(board) 317 | 318 | ## Other Similar Reference 319 | 320 | * [ESP_IDF](https://github.com/espressif/esp-idf): `SDK` of `ESP32`, Written very well 321 | * [RT-Thread](https://github.com/RT-Thread/rt-thread):not `CMake`, but also use component 322 | 323 | -------------------------------------------------------------------------------- /README_ZH.md: -------------------------------------------------------------------------------- 1 | C CPP Project Framework (Template) 2 | =================== 3 | 4 | [English](./README.md) 5 | 6 | 一个足够 **简单易用** 并且 **可配置的**用于构建 `C/C++` 的模板工程 7 | > 使用 `CMake` 构建,并且支持带`GUI`配置界面的 `Kconfig` 8 | 9 | 基于此工程,可以快速搭建你的项目构建系统,减少许多不必要的麻烦~ 10 | 11 | 希望能帮到你,如果帮到了请右上角给颗小星星哦 ~~ 如果有啥不足欢迎提 [issue](https://github.com/Neutree/c_cpp_project_framework/issues/new)。 `(´ε` ʃƪ)♡` 12 | 13 | 本项目的目标人群: 14 | 15 | * 正准备写 `SDK` 并且需要自己写构建系统的开发者 16 | * 正准备开始编写一个需要写构建系统的开发者 17 | * 对`CMake`不太了解,但想学习 `CMake` 的开发者 18 | * 想重构代码工程构建系统的,比如因为之前写的构建系统太杂乱 19 | * 想给构建系统中加一个十分好用的配置系统,可以按需求快速增删代码模块的,而且最好带界面配置的 开发者 20 | * 想让项目可以生成多种 `IDE` 支持的工程 21 | 22 | 23 | ![](assets/image/build.gif) 24 | 25 | 26 | ## 特性 27 | 28 | * 语法简单, 无需`Makefile`或者`CMake`知识, 只需基于模板修改变量的值即可非常简单地用起来了 29 | * 基于组件(component)概念的项目结构, 方便搭建层次结构清晰的项目架构 30 | * 组件之间调用只需要一个语句指定依赖即可(比如`list(APPEND ADD_REQUIREMENTS component1)`), 无需设置多余变量比如`INCLUDE 路径`即可在源码中调用依赖的组件的内容 31 | * 使用 `Kconfig` 使项目组件和代码可裁剪配置, 方便项目满足不同的需求 32 | * 方便地引入静态库(`.a`) 和 动态库(`.so`) ( 比如`list(APPEND ADD_STATIC_LIB "lib/libtest.a")`) 33 | * 方便地生成静态库(`.a`) 和 动态库(`.so`) (默认生成静态库, 需要组件生成动态库,则使用`register_component(DYNAMIC)`注册模块即可) 34 | * 使用 `Python` 脚本作为辅助, 可方便地添加命令和工具, 编译只需要执行简单的命令即可(如`python project.py build` `python project.py menuconfig`) 35 | * 方便地作为 `SDK`, 工程实例可以直接放在`SDK`目录下, 也可以单独放在磁盘任何地方, 只需要设置环境变量`MY_SDK_PATH`即可 36 | * 交叉编译友好, 很好地作为嵌入式设备 `SDK` 37 | * 生成各种 `IDE` 可以直接使用的工程文件, 可以方便地导入到各种 `IDE` 中使用 38 | * 支持编译成 `WASM`(Web Assembly) 39 | * 支持 VSCode + GDB 在线调试(PC 或者嵌入式设备均支持) 40 | 41 | 42 | ## 快速上手 43 | 44 | * 克隆代码: 45 | ``` 46 | git clone https://github.com/Neutree/c_cpp_project_framework --recursive 47 | ``` 48 | > 参数 `--recursive` 是必须的, 是为了克隆子模块, 否则克隆下来的代码不完整 49 | 50 | * 或者基于这个模板仓库创建一个你自己的 github 仓库:
点击 `use this template` 按钮即可
![](assets/image/use_template.png) 51 | 52 | * 开始编译
有两种方法, 使用`project.py`脚本或者使用原生`CMake`命令 53 | * 使用`project.py`脚本 54 | ``` 55 | cd examples/demo1 56 | # python project.py --toolchain /opt/toolchain/bin --toolchain-prefix mips-elf- config 57 | python project.py menuconfig 58 | python project.py build 59 | # python project.py rebuild # 当删减了文件时, 需要使用此命令而不是 build 命令 60 | # 可以使用 --verbose 参数来打印编译时的调试信息,在编译出现问题时十分有用 61 | # python project.py build --verbose 62 | python project.py run # 或者 ./build/demo1 63 | python project.py clean 64 | python project.py distclean 65 | # python project.py clean_conf 66 | ``` 67 | * 切换工程目录 68 | * 设置工具链路径以及前缀(如果使用`gcc`不需要设置) 69 | * 使用命令 `python project.py menuconfig` 来通过终端图形界面配置工程, 这会在 `build/config` 目录生成几个配置文件(`global_config.*`), 我们可以直接在组件(`component`)的`CMakelists.txt` 文件中直接使用(详细看后面的说明), 或者在 `C/CPP`源文件中通过语句 `#include "global_config.h"` 包含配置头文件来使用 70 | * 使用命令 `python project.py build` 来启动构建, 可以使用`python project.py build --verbose`命令来打印编译时的调试信息, 在遇到编译错误时十分有用 71 | * 使用`python project.py clean`来清除构建中间文件, 使用`python project.py distclean`来清楚`menuconfig`命令生成的文件, 这个命令不会清除工具链配置 72 | * 使用`python project.py clean_conf`来清除工具链配置 73 | * 使用原生`CMake`命令 74 | ``` 75 | cd examples/demo1 76 | # python project.py --toolchain /opt/toolchain/bin --toolchain-prefix mips-elf- config 77 | mkdir build && cd build 78 | cmake .. 79 | make menuconfig 80 | make -j10 81 | ./build/demo1 82 | make clean 83 | rm -rf ./* 84 | ``` 85 | * 切换工程目录 86 | * 设置工具链路径以及前缀(如果使用`gcc`不需要设置) 87 | * 建立一个临时目录并且切换当前路径到这个临时目录(`build`) 88 | * 使用命令 `cmake ..` 来生成 `Makefile`, 这里 `..` 表示上层目录,即项目的目录 89 | * 使用命令 `make menuconfig` 来通过终端图形界面配置工程, 这会在 `build/config` 目录生成几个配置文件(`global_config.*`), 我们可以直接在组件(`component`)的`CMakelists.txt` 文件中直接使用(详细看后面的说明), 或者在 `C/CPP`源文件中通过语句 `#include "global_config.h"` 包含配置头文件来使用 90 | * 使用命令 `make` 来执行编译链接过程, 或者并行编译: [make -jN](http://www.gnu.org/software/make/manual/make.html#Parallel), 以及通过 `make VERBOSE=1` 命令来打印编译时的调试信息 91 | 92 | 93 | ## 目录结构 94 | 95 | | 目录/文件 | 功能 | 96 | | -------------- | -------- | 97 | | 根目录 | 本项目的根目录,也是 `SDK` 项目的 `SDK` 目录| 98 | | assets | 存放多媒体资源的文件夹,比如图片等,如果不需要可以删除 | 99 | | components | 组件(component)都放在这里 | 100 | | examples | 工程目录,或者例程目录;在 `SDK` 项目中这个目录是可以和 `SDK` 目录分开放的, 只需要设置环境变量`MY_SDK_PATH`为`SDK`目录路径即可 | 101 | | tools | 工具目录比如 `cmake`、`kconfig`、`burn tool` 等等 | 102 | | Kconfig | `Kconfig` 的最顶层配置 | 103 | 104 | ### 1) 组件(component) 105 | 106 | 所有库均作为组件(component)被放在`components`目录下或者工程目录下,每个组件用一个目录,这个目录就是这个组件的名字, 为了使工程看起来更简洁,不对组件进行嵌套,所有组件都是一个层级,组件之间的关系依靠依赖关系(requirements)来维持 107 | 108 | 所有源文件都必须在某个组件内,每个工程必须包含一个叫 `main` 的组件(即`examples/demo1/main` 目录),每个组件包含文件如下: 109 | 110 | * `CMakeLists.txt`: 必须有,声明组件源文件以及依赖的组件,并且调用注册函数注册自己,详细可以参考`components/component1`和`components/component2`下`CMakeLists.txt`的写法 111 | 112 | * `Kconfig`: 可选,包含了本组件的配置选项, 在本组件或者其它依赖了本组件的组件的`CMakeLists.txt`中都可以在加一个`CONFIG_`前缀后使用这些配置项,比如在`components/component2`中,`Kconfig`中有`COMPONENT2_ENABLED` 选项,我们在它的`CMakeLists.txt`中就使用了这个变量`if(CONFIG_COMPONENT2_ENABLED)`来判断如果用户配置不用这个组件就不注册这个组件 113 | 114 | ### 2) 工程目录 115 | 116 | 工程目录在`examples`目录下,当然,这个目录的名字是可以随意根据实际需求修改的,下面可以包含多个实际的工程目录,需要编译那个工程时切换到对应的目录就可以编译。上面也说了,每个工程目录下必须有一个 `main` 组件, 当然也可以放很多自定义的组件。 可以参考`examples/demo1`工程目录。 117 | 118 | 工程目录下文件: 119 | 120 | * `CMakeLists.txt`: 工程属性文件,必须先包含`include(${SDK_PATH}/tools/cmake/compile.cmake)`,然后用`project`函数声明工程名称,比如`project(demo1)`,当然还可以编写其它的条件或者变量等,使用`CMake`语法, 参考`examples/demo1/CMakeLists.txt`的写法 121 | 122 | * `config_defaults.mk`: 工程默认配置文件,执行`cmake`构建时会从这里加载默认配置,配置的格式是`Makefile`的格式,可以先使用终端界面配置(`make menuconfig`)生成配置文件复制过来,生成的配置文件在`build/config/global_config.mk`。 123 | > 注意:每次修改`config_defaults.mk` 后需要删除`build`目录下的文件(或者只删除`build/config/global_config.mk`文件)重新生成,因为当前构建系统会优先使用`build`目录下已经存在的配置文件 124 | 125 | * `project.py`: 工具脚本调用入口, 使用`python project.py menuconfig` `python project.py build` 等命令来开始构建 126 | 127 | 如何将工程目录放在磁盘的任何地方: 128 | 129 | * 将`CMakeLists.txt`和 `project.py` 中的 `MY_SDK_PATH` 改成你喜欢的环境变量名称, 然后在终端中设置这个环境变量的值为`SDK`的路径, 即可将这个工程目录放到任何地方也可以编译了 130 | 131 | ## SDK 和 工程目录分开存放 132 | 133 | 通常情况下,只需要按照自己的需求,修改`example`目录的名字,比如改成`projects`,或者在工程根目录重新创建一个目录也是可以的,比如`projects/hello_world`,然后拷贝`examples/demo1`中的内容来新建一个新的工程 134 | 135 | 另外,工程目录和 SDK 目录也可以分开存放,这通常适用于开源项目,一份SDK,用户基于这份 SDK 开发,这样更利于源码传播,用户不需要拷贝一份 SDK, 只需要指定使用的 SDK 版本(git 提交号)。 136 | 要做到,只需要: 137 | 138 | * 下载 `SDK` 放到某个目录,比如 `/home/neucrack/my_SDK` 139 | 140 | ``` 141 | git clone https://github.com/Neutree/c_cpp_project_framework --recursive 142 | ``` 143 | 注意这里用了`--recursive`参数, 因为工程中使用了子模块,子模块的好处是各个工程分开管理,比如这里用了`Kconfiglib`作为子模块,提供`menuconfig`带界面的功能配置功能 144 | 145 | **注意如果不更新子模块,将无法通过编译!!!** 146 | 147 | 如果克隆的时候忘记加这个参数了,也可以再使用下面这个命令来更新子模块: 148 | ``` 149 | git submodule update --init --recursive 150 | ``` 151 | 另外,当远程仓库更新了,用户也需要使用以下命令来更新代码(即同时更新子模块代码): 152 | ```shell 153 | git pull --recursive 154 | ``` 155 | 或者: 156 | ``` 157 | git pull 158 | git submodule update --init --recursive 159 | ``` 160 | 161 | 当然,你也可以选择删除 `.git` 目录,然后重新创建一个没有子模块的 git 仓库~ 162 | 163 | * 然后在终端导出变量 `export MY_SDK_PATH=/home/neucrack/my_SDK`, 可以放到 `~/.bashrc`或者`~/.zshrc`文件中,这样每次终端启动都会自动添加这个变量了 164 | * 然后在任意地方建立工程, 比如拷贝`example/demo1`整个文件夹中的所有文件到`/home/neucrack/temp/my_projects/demo1` 165 | * 然后清除之前的构建缓存(如果有的话,没有就忽略) 166 | ``` 167 | python3 project.py distclean 168 | ``` 169 | * 然后配置和构建即可 170 | ``` 171 | python3 project.py menuconfig 172 | python3 project.py build 173 | ``` 174 | 175 | ## 设置自定义组件库路径 176 | 177 | 一般来说通用的组件放在 `SDK 目录 -> components 目录`下, 而工程特有的组件放在 `工程目录`下。 178 | 除此之外,用户还可以自定义自己的通用组件的存放位置,通过在系统环境变量设置 `CUSTOM_COMPONENTS_PATH` 来指定自定义组件的存放位置,比如: 179 | Linux 下: 180 | ``` 181 | export CUSTOM_COMPONENTS_PATH=/home/neucrack/my_components 182 | ``` 183 | Windows 直接在环境变量界面中添加`CUSTOM_COMPONENTS_PATH`变量即可。 184 | > `CUSTOM_COMPONENTS_PATH`这个名称可以在工程的`project.py`和`CMakeLists.txt`中根据你的项目名称或者喜好修改。 185 | 186 | 然后就能在工程的组件里面直接通过 `list(APPEND ADD_REQUIREMENTS 组件名)` 来引用了。 187 | 188 | ## 调试 (Debug) 版本和发布 (Release) 版本 189 | 190 | 默认都是以 debug 版本编译,如果要发布版本,可以使用以下命令: 191 | ```shell 192 | python project.py distclean 193 | python project.py build --release 194 | ``` 195 | 196 | 此时构建的二进制文件就是 release 版本,编译脚本做了几个动作: 197 | * 设置 CMake 环境变量 `CMAKE_BUILD_TYPE` 为 `MinSizeRel`(默认是 `Debug`) 198 | * 在生成的头文件`global_config.h`中添加了 `#define RELEASE 1`(默认会加`#define DEBUG 1`) 199 | * 在编译时自动添加了`RELEASE=1`的宏定义,所以代码其实不用引入`global_config.h`也可以通过`RELEASE`和`DEBUG`宏定义判断当前是 release 版本还是 debug 版本 200 | 201 | 202 | ## 更换工程生成器 203 | 204 | 有时你可能需要更快的构建速度,或者需要生成一个工程给 IDE 使用,比如 Visual Studio, 205 | 可以通过更改工程生成器实现, 默认的生成器是 `Unix Makefiles`。 206 | 207 | 有多种生成器选择,比如 `Ninja`, `Visual Studio`, `Xcode`, `Eclipse`, `Unix Makefiles` 等等。 208 | 执行命令 `cmake --help` 可以查看生成器选择,不同的系统支持不同的生成器。 209 | 比如 Linux 下: 210 | ``` 211 | Generators 212 | 213 | The following generators are available on this platform (* marks default): 214 | Green Hills MULTI = Generates Green Hills MULTI files 215 | (experimental, work-in-progress). 216 | * Unix Makefiles = Generates standard UNIX makefiles. 217 | Ninja = Generates build.ninja files. 218 | Ninja Multi-Config = Generates build-.ninja files. 219 | Watcom WMake = Generates Watcom WMake makefiles. 220 | CodeBlocks - Ninja = Generates CodeBlocks project files. 221 | CodeBlocks - Unix Makefiles = Generates CodeBlocks project files. 222 | CodeLite - Ninja = Generates CodeLite project files. 223 | CodeLite - Unix Makefiles = Generates CodeLite project files. 224 | Eclipse CDT4 - Ninja = Generates Eclipse CDT 4.0 project files. 225 | Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files. 226 | Kate - Ninja = Generates Kate project files. 227 | Kate - Unix Makefiles = Generates Kate project files. 228 | Sublime Text 2 - Ninja = Generates Sublime Text 2 project files. 229 | Sublime Text 2 - Unix Makefiles 230 | = Generates Sublime Text 2 project files. 231 | ``` 232 | 233 | 通过 `config` 命令来更改生成器 234 | ``` 235 | # 首先清理干净之前的构建缓存(就是删除 build 目录) 236 | python project.py distclean 237 | 238 | python project.py -G Ninja config 239 | # python project.py -G "Eclipse CDT4 - Ninja" config 240 | 241 | python project.py build 242 | ``` 243 | 244 | ## 编译成 WASM 245 | 246 | 根据 [emscripten-core/emsdk](https://github.com/emscripten-core/emsdk) 先安装工具链 247 | ``` 248 | git clone https://github.com/emscripten-core/emsdk.git 249 | ./emsdk install latest 250 | ./emsdk activate latest 251 | ``` 252 | 253 | 只需要 254 | ``` 255 | python project.py distclean 256 | python project.py --toolchain $EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake config 257 | python project.py build 258 | ``` 259 | 260 | 在 `build` 目录就会出现`demo1.html`, `demo1.js` 和 `demo1.wasm`文件了,直接运行以下命令就会在浏览器执行结果并显示结果了 261 | ``` 262 | emrun demo1.html 263 | ``` 264 | 265 | 或者也可以直接使用`node`即可运行 266 | ``` 267 | node demo1.js 268 | ``` 269 | 270 | ## 增加新的命令 271 | 272 | 比如默认使用 `python project.py run` 命令会调用`tools/cmds/run.py`脚本来执行构建出来的可执行文件。 273 | 如果你需要给你的 SDK 增加命令,只需要创建一个新的文件,参考[tools/cmds/run.py](./tools/cmds/run.py)文件的写法即可 274 | 275 | ## 在线调试 276 | 277 | ### VSCode + GDB 在线调试 278 | 279 | 这里以 PC 为 Linux 系统为例: 280 | 281 | * 添加 c_cpp_project_framework(第一次试用推荐这样) 或者 工程目录到 VSCode 工作区 282 | * 拷贝 [./assets/vscode_local_debug/.vscode](./assets/vscode_local_debug/.vscode) 目录到上一步的工作目录下 283 | * 根据`.vscode`是在 c_cpp_project_framework 还是在工程目录下,修改`.vscode/launch.json`中的`cwd`字段 284 | * 按键盘 `F5` 即可开始调试 285 | > windows 也类似,修改`.vscode`里面的相关命令和路径即可 286 | 287 | ### VSCode + gdbserver 在嵌入式设备(/远程设备,带 Linux 系统)调试 288 | 289 | 这里以 PC 为 Linux 系统为例: 290 | 291 | * 先保证远程设备有`gdbserver`这个程序,以及 PC 有`gdb-multiarch`这个程序 292 | * 将 [./assets/vscode_remote_debug/.vscode](./assets/vscode_remote_debug/.vscode) 目录拷贝到工程目录下 293 | * 编辑 `launch.json` 和 `build_run_gdbserver.sh` 文件,修改里面的路径和命令,以及用户名等。 294 | > 建议先将 PC 的 ssh key 加入到远程设备的 `~/.ssh/authorized_keys` 文件中,这样就不需要输入密码了。 295 | * 每次调试需要执行 `build_run_gdbserver.sh` 脚本,然后在 VSCode 中按 `F5` 即可开始调试 296 | > 脚本会编译工程,然后拷贝可执行文件到远程设备,并且启动 `gdbserver`。 297 | > 按 F5 启动调试时, VSCode 使用 GDB 连接到远程设备的`gdbserver`以调试。 298 | 299 | 300 | ## 开源许可 301 | 302 | **MIT**: 详情看 [LICENSE](./LICENSE) 文件 303 | 304 | ## 此工程使用到的开源项目 305 | 306 | * [Kconfiglib](https://github.com/ulfalizer/Kconfiglib): `Kconfig` `Python` 实现 307 | 308 | ## 使用了此框架的仓库 309 | 310 | * [Maix-Speech](https://github.com/sipeed/Maix-Speech): 专为嵌入式环境设计的离线语音库 311 | * [MaixPy](https://github.com/sipeed/MaixPy/): 让针对`AIOT`应用的芯片`K210`用上`Micropython` 312 | * [libmaix](https://github.com/sipeed/libmaix): 支持多嵌入式平台的带硬件加速的 AI 模型运行库 313 | * [MF1_SDK](https://github.com/sipeed/MF1_SDK): `MF1` AI 模组(开发板)的 SDK 314 | 315 | 316 | ## 相关类似参考项目 317 | 318 | * [ESP_IDF](https://github.com/espressif/esp-idf): `ESP32` 的 `SDK`, 写得挺不错 319 | * [RT-Thread](https://github.com/RT-Thread/rt-thread):不是用的 `CMake`, 但是也是使用了组件的概念 320 | 321 | -------------------------------------------------------------------------------- /assets/image/build.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neutree/c_cpp_project_framework/435427099998a90fa61e27af48a6565da82a29fe/assets/image/build.gif -------------------------------------------------------------------------------- /assets/image/use_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neutree/c_cpp_project_framework/435427099998a90fa61e27af48a6565da82a29fe/assets/image/use_template.png -------------------------------------------------------------------------------- /assets/vscode_local_debug/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Debug", 9 | "type": "gdb", 10 | "request": "launch", 11 | "preLaunchTask": "rebuild", 12 | "target": "build/demo1", 13 | "cwd": "${workspaceRoot}/examples/demo1", 14 | "valuesFormatting": "parseText" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /assets/vscode_local_debug/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": [ 3 | { 4 | "type": "shell", 5 | "label": "rebuild", 6 | "command": "python", 7 | "args": [ 8 | "project.py", 9 | "rebuild" 10 | ], 11 | "options": { 12 | "cwd": "${workspaceRoot}/examples/demo1" 13 | }, 14 | "problemMatcher": [ 15 | "$gcc" 16 | ], 17 | "group": { 18 | "kind": "build", 19 | "isDefault": true 20 | } 21 | } 22 | ], 23 | "version": "2.0.0" 24 | } -------------------------------------------------------------------------------- /assets/vscode_remote_debug/.vscode/build_run_gdbserver.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | 3 | set -e 4 | 5 | ##################### config ##################### 6 | project_path=$(pwd)/../examples/demo1 7 | program_path=${project_path}/build/demo1 8 | target_ip=rock-5b.local 9 | target_port=9000 10 | target_user=root 11 | ################################################## 12 | 13 | ## build first 14 | cd ${project_path} 15 | # python project.py --toolchain "" --toolchain-prefix aarch64-linux-gnu- config 16 | python project.py rebuild 17 | 18 | # kill gdbserver first if it is running 19 | ssh ${target_user}@${target_ip} "killall gdbserver > /dev/null 2>&1 || true" 20 | 21 | ## copy to target 22 | scp ${program_path} ${target_user}@${target_ip}:~/dbg_program.bin 23 | 24 | ## run gdbserver 25 | ssh ${target_user}@${target_ip} "gdbserver :${target_port} ~/dbg_program.bin" 26 | 27 | 28 | ## now you can run debug in vscode 29 | 30 | 31 | -------------------------------------------------------------------------------- /assets/vscode_remote_debug/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Debug Program On Remote", 9 | "type": "gdb", 10 | "request": "attach", 11 | "gdbpath":"gdb-multiarch", 12 | "executable": "${workspaceRoot}/examples/demo1/build/demo1", 13 | "target": "rock-5b.local:9000", 14 | "remote": true, 15 | "printCalls": true, 16 | "cwd": "${workspaceRoot}/examples/demo1", 17 | "valuesFormatting": "parseText", 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /components/component1/.gitignore: -------------------------------------------------------------------------------- 1 | !lib/*.a 2 | -------------------------------------------------------------------------------- /components/component1/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ################# Add include ################# 2 | list(APPEND ADD_INCLUDE "include" 3 | ) 4 | # list(APPEND ADD_PRIVATE_INCLUDE "include_private") 5 | ############################################### 6 | 7 | ############## Add source files ############### 8 | list(APPEND ADD_SRCS "src/lib1.c" 9 | ) 10 | # FILE(GLOB_RECURSE EXTRA_SRC "src/*.c") 11 | # FILE(GLOB EXTRA_SRC "src/*.c") 12 | # list(APPEND ADD_SRCS ${EXTRA_SRC}) 13 | # aux_source_directory(src ADD_SRCS) # collect all source file in src dir, will set var ADD_SRCS 14 | # append_srcs_dir(ADD_SRCS "src") # append source file in src dir to var ADD_SRCS 15 | # list(REMOVE_ITEM COMPONENT_SRCS "src/test.c") 16 | # set(ADD_ASM_SRCS "src/asm.S") 17 | # list(APPEND ADD_SRCS ${ADD_ASM_SRCS}) 18 | # SET_PROPERTY(SOURCE ${ADD_ASM_SRCS} PROPERTY LANGUAGE C) # set .S ASM file as C language 19 | # SET_SOURCE_FILES_PROPERTIES(${ADD_ASM_SRCS} PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp -D BBBBB") 20 | ############################################### 21 | 22 | ###### Add required/dependent components ###### 23 | # list(APPEND ADD_REQUIREMENTS component1) 24 | # 25 | # list(APPEND ADD_FILE_DEPENDS include/axx.h) 26 | # set_property(SOURCE ${python_h_path} PROPERTY GENERATED 1) 27 | # add_custom_command(OUTPUT include/axx.h 28 | # COMMAND echo "" > include/axx.h 29 | # COMMENT "Generating axx.h ..." 30 | # ) 31 | ############################################### 32 | 33 | ###### Add link search path for requirements/libs ###### 34 | # list(APPEND ADD_LINK_SEARCH_PATH "${CONFIG_TOOLCHAIN_PATH}/lib") 35 | list(APPEND ADD_REQUIREMENTS m) # add system libs, pthread or m(math) lib for example 36 | # set (OpenCV_DIR opencv/lib/cmake/opencv4) 37 | # find_package(OpenCV REQUIRED) 38 | ############################################### 39 | 40 | ############ Add static libs ################## 41 | if(CONFIG_COMPONENT1_INCLUDE_STATIC_LIB) 42 | list(APPEND ADD_STATIC_LIB "lib/libtest.a") 43 | endif() 44 | ############################################### 45 | 46 | ############ Add dynamic libs ################## 47 | # list(APPEND ADD_DYNAMIC_LIB "lib/arch/v831/libmaix_nn.so" 48 | # "lib/arch/v831/libmaix_cam.so" 49 | # ) 50 | ############################################### 51 | 52 | #### Add compile option for this component #### 53 | #### Just for this component, won't affect other 54 | #### modules, including component that depend 55 | #### on this component 56 | list(APPEND ADD_DEFINITIONS_PRIVATE -DAAAAA=1) 57 | 58 | #### Add compile option for this component 59 | #### and components denpend on this component 60 | list(APPEND ADD_DEFINITIONS -DAAAAA222=1 61 | -DAAAAA333=1) 62 | ############################################### 63 | 64 | ############ Add static libs ################## 65 | #### Update parent's variables like CMAKE_C_LINK_FLAGS 66 | # set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--start-group libmaix/libtest.a -ltest2 -Wl,--end-group" PARENT_SCOPE) 67 | ############################################### 68 | 69 | # register component, DYNAMIC or SHARED flags will make component compiled to dynamic(shared) lib 70 | register_component() 71 | 72 | -------------------------------------------------------------------------------- /components/component1/Kconfig: -------------------------------------------------------------------------------- 1 | menu "Component1 configuration" 2 | config COMPONENT1_INCLUDE_STATIC_LIB 3 | bool "Enable include static lib(from gcc)" 4 | default n 5 | help 6 | Enable include static lib(from gcc) 7 | endmenu 8 | 9 | -------------------------------------------------------------------------------- /components/component1/include/lib1.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void test1(); 4 | -------------------------------------------------------------------------------- /components/component1/lib/libtest.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neutree/c_cpp_project_framework/435427099998a90fa61e27af48a6565da82a29fe/components/component1/lib/libtest.a -------------------------------------------------------------------------------- /components/component1/src/lib1.c: -------------------------------------------------------------------------------- 1 | 2 | #include "lib1.h" 3 | #include "stdio.h" 4 | #include "global_config.h" 5 | 6 | #include "math.h" 7 | // need math lib named libm, we need to add `list(APPEND ADD_REQUIREMENTS m)` in CmakeLists.txt 8 | 9 | static float math_exp(float x) 10 | { 11 | return expf(x); 12 | } 13 | 14 | void test1() 15 | { 16 | float x = 1.23; 17 | printf("lib1 test1: e^%f = %f\n", x, math_exp(x)); 18 | #if AAAAA 19 | printf("AAAAAA\n"); 20 | #endif 21 | #if AAAAA222 22 | printf("AAAAAA222\n"); 23 | #endif 24 | 25 | } 26 | 27 | -------------------------------------------------------------------------------- /components/component2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Config enable component2 or not in Kconfig 2 | if(CONFIG_COMPONENT2_ENABLED) 3 | 4 | ################# Add include ################# 5 | list(APPEND ADD_INCLUDE "include" 6 | ) 7 | list(APPEND ADD_PRIVATE_INCLUDE "include_private") 8 | ############################################### 9 | 10 | ############## Add source files ############### 11 | list(APPEND ADD_SRCS "src/lib2.c" 12 | ) 13 | # FILE(GLOB_RECURSE EXTRA_SRC "src/*.c") 14 | # FILE(GLOB EXTRA_SRC "src/*.c") 15 | # list(APPEND ADD_SRCS ${EXTRA_SRC}) 16 | # aux_source_directory(src ADD_SRCS) # collect all source file in src dir, will set var ADD_SRCS 17 | # append_srcs_dir(ADD_SRCS "src") # append source file in src dir to var ADD_SRCS 18 | # list(REMOVE_ITEM COMPONENT_SRCS "src/test.c") 19 | # set(ADD_ASM_SRCS "src/asm.S") 20 | # list(APPEND ADD_SRCS ${ADD_ASM_SRCS}) 21 | # SET_PROPERTY(SOURCE ${ADD_ASM_SRCS} PROPERTY LANGUAGE C) # set .S ASM file as C language 22 | # SET_SOURCE_FILES_PROPERTIES(${ADD_ASM_SRCS} PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp -D BBBBB") 23 | ############################################### 24 | 25 | 26 | ###### Add required/dependent components ###### 27 | list(APPEND ADD_REQUIREMENTS component1) 28 | # 29 | # list(APPEND ADD_FILE_DEPENDS include/axx.h) 30 | # set_property(SOURCE ${python_h_path} PROPERTY GENERATED 1) 31 | # add_custom_command(OUTPUT include/axx.h 32 | # COMMAND echo "" > include/axx.h 33 | # COMMENT "Generating axx.h ..." 34 | # ) 35 | ############################################### 36 | 37 | ###### Add link search path for requirements/libs ###### 38 | # list(APPEND ADD_LINK_SEARCH_PATH "${CONFIG_TOOLCHAIN_PATH}/lib") 39 | # list(APPEND ADD_REQUIREMENTS pthread m) # add system libs, pthread and math lib for example here 40 | # set (OpenCV_DIR opencv/lib/cmake/opencv4) 41 | # find_package(OpenCV REQUIRED) 42 | ############################################### 43 | 44 | ############ Add static libs ################## 45 | # list(APPEND ADD_STATIC_LIB "lib/libtest.a") 46 | ############################################### 47 | 48 | ############ Add dynamic libs ################## 49 | # list(APPEND ADD_DYNAMIC_LIB "lib/arch/v831/libmaix_nn.so" 50 | # "lib/arch/v831/libmaix_cam.so" 51 | # ) 52 | ############################################### 53 | 54 | #### Add compile option for this component #### 55 | #### Just for this component, won't affect other 56 | #### modules, including component that depend 57 | #### on this component 58 | # list(APPEND ADD_DEFINITIONS_PRIVATE -DAAAAA=1) 59 | 60 | #### Add compile option for this component 61 | #### and components denpend on this component 62 | # list(APPEND ADD_DEFINITIONS -DAAAAA222=1 63 | # -DAAAAA333=1) 64 | ############################################### 65 | 66 | ############ Add static libs ################## 67 | #### Update parent's variables like CMAKE_C_LINK_FLAGS 68 | # set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--start-group libmaix/libtest.a -ltest2 -Wl,--end-group" PARENT_SCOPE) 69 | ############################################### 70 | 71 | # register component, DYNAMIC or SHARED flags will make component compiled to dynamic(shared) lib 72 | register_component() 73 | endif() 74 | 75 | -------------------------------------------------------------------------------- /components/component2/Kconfig: -------------------------------------------------------------------------------- 1 | menu "Component2 configuration" 2 | config COMPONENT2_ENABLED 3 | bool "Enable component2" 4 | help 5 | Select this option to enable component2 and show the submenu. 6 | menu "Component2 configuration menu2" 7 | visible if COMPONENT2_ENABLED 8 | choice COMPONENT2_TEST_STR 9 | prompt "Component2 test string" 10 | depends on COMPONENT2_ENABLED 11 | help 12 | Component2 test string in lib2_test func. 13 | 14 | config COMPONENT2_TEST_STR1 15 | bool "component2 test string 1" 16 | 17 | config COMPONENT2_TEST_STR2 18 | bool "component2 test string 2" 19 | 20 | config COMPONENT2_TEST_STR3 21 | bool "component2 test string 3" 22 | endchoice 23 | endmenu 24 | endmenu 25 | 26 | 27 | menuconfig MENUCONFIG_EXAMPLE 28 | bool "selectable menuconfig item example" 29 | default n 30 | 31 | config MENUCONFIG_SUB_EXAMPLE_1 32 | bool "menuconfig item's sub item1" 33 | default y 34 | depends on MENUCONFIG_EXAMPLE 35 | config MENUCONFIG_SUB_EXAMPLE_2 36 | bool "menuconfig item's sub item2" 37 | default n 38 | depends on MENUCONFIG_EXAMPLE 39 | 40 | 41 | -------------------------------------------------------------------------------- /components/component2/include/lib2.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIB2_H 2 | #define __LIB2_H 3 | 4 | void lib2_test(); 5 | 6 | #endif 7 | 8 | -------------------------------------------------------------------------------- /components/component2/include_private/lib2_private.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neutree/c_cpp_project_framework/435427099998a90fa61e27af48a6565da82a29fe/components/component2/include_private/lib2_private.h -------------------------------------------------------------------------------- /components/component2/src/lib2.c: -------------------------------------------------------------------------------- 1 | 2 | #include "lib1.h" 3 | #include "lib2.h" 4 | #include "lib2_private.h" 5 | #include "stdio.h" 6 | #include "global_config.h" 7 | #include "global_build_info_time.h" 8 | #include "global_build_info_version.h" 9 | 10 | 11 | void lib2_test() 12 | { 13 | printf("lib2_test\r\n"); 14 | #ifdef CONFIG_COMPONENT2_TEST_STR1 15 | printf("lib2 test string 1\r\n"); 16 | #elif defined CONFIG_COMPONENT2_TEST_STR2 17 | printf("lib2 test string 2\r\n"); 18 | #elif defined CONFIG_COMPONENT2_TEST_STR3 19 | printf("lib2 test string 3\r\n"); 20 | #endif 21 | printf("build time:%d-%d-%d %d:%d:%d\r\n", BUILD_TIME_YEAR, BUILD_TIME_MONTH, BUILD_TIME_DAY, 22 | BUILD_TIME_HOUR, BUILD_TIME_MINUTE, BUILD_TIME_SECOND); 23 | printf("git info:v%d.%d.%d-%d %s \r\n", BUILD_VERSION_MAJOR, BUILD_VERSION_MINOR, BUILD_VERSION_MICRO, 24 | BUILD_VERSION_DEV, BUILD_GIT_COMMIT_ID); 25 | if(BUILD_VERSION_MAJOR == 0 && 26 | BUILD_VERSION_MINOR == 0 && 27 | BUILD_VERSION_MICRO == 0 && 28 | BUILD_VERSION_DEV == 0) 29 | { 30 | printf("no tag, create by command: git tag -a v0.1.1 -m \"release v0.1.1 describe.....\"\r\n"); 31 | } 32 | #if BUILD_GIT_IS_DIRTY 33 | printf("WARNING: git repository have file not commit when build\r\n"); 34 | #endif 35 | #if AAAAA 36 | printf("AAAAAA222222\n"); 37 | #endif 38 | } 39 | 40 | -------------------------------------------------------------------------------- /components/component3/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Config enable component3 or not in Kconfig 2 | if(CONFIG_COMPONENT3_ENABLED) 3 | 4 | ################# Add include ################# 5 | list(APPEND ADD_INCLUDE "include" 6 | ) 7 | list(APPEND ADD_PRIVATE_INCLUDE "include_private") 8 | ############################################### 9 | 10 | ############## Add source files ############### 11 | list(APPEND ADD_SRCS "src/lib3.c" 12 | ) 13 | # FILE(GLOB_RECURSE EXTRA_SRC "src/*.c") 14 | # FILE(GLOB EXTRA_SRC "src/*.c") 15 | # list(APPEND ADD_SRCS ${EXTRA_SRC}) 16 | # aux_source_directory(src ADD_SRCS) # collect all source file in src dir, will set var ADD_SRCS 17 | # append_srcs_dir(ADD_SRCS "src") # append source file in src dir to var ADD_SRCS 18 | # list(REMOVE_ITEM COMPONENT_SRCS "src/test.c") 19 | # set(ADD_ASM_SRCS "src/asm.S") 20 | # list(APPEND ADD_SRCS ${ADD_ASM_SRCS}) 21 | # SET_PROPERTY(SOURCE ${ADD_ASM_SRCS} PROPERTY LANGUAGE C) # set .S ASM file as C language 22 | # SET_SOURCE_FILES_PROPERTIES(${ADD_ASM_SRCS} PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp -D BBBBB") 23 | ############################################### 24 | 25 | 26 | ###### Add required/dependent components ###### 27 | list(APPEND ADD_REQUIREMENTS component1) 28 | # 29 | # list(APPEND ADD_FILE_DEPENDS include/axx.h) 30 | # set_property(SOURCE ${python_h_path} PROPERTY GENERATED 1) 31 | # add_custom_command(OUTPUT include/axx.h 32 | # COMMAND echo "" > include/axx.h 33 | # COMMENT "Generating axx.h ..." 34 | # ) 35 | ############################################### 36 | 37 | ###### Add link search path for requirements/libs ###### 38 | # list(APPEND ADD_LINK_SEARCH_PATH "${CONFIG_TOOLCHAIN_PATH}/lib") 39 | # list(APPEND ADD_REQUIREMENTS pthread m) # add system libs, pthread and math lib for example here 40 | # set (OpenCV_DIR opencv/lib/cmake/opencv4) 41 | # find_package(OpenCV REQUIRED) 42 | ############################################### 43 | 44 | ############ Add static libs ################## 45 | # list(APPEND ADD_STATIC_LIB "lib/libtest.a") 46 | ############################################### 47 | 48 | ############ Add dynamic libs ################## 49 | # list(APPEND ADD_DYNAMIC_LIB "lib/arch/v831/libmaix_nn.so" 50 | # "lib/arch/v831/libmaix_cam.so" 51 | # ) 52 | ############################################### 53 | 54 | #### Add compile option for this component #### 55 | #### Just for this component, won't affect other 56 | #### modules, including component that depend 57 | #### on this component 58 | # list(APPEND ADD_DEFINITIONS_PRIVATE -DAAAAA=1) 59 | 60 | #### Add compile option for this component 61 | #### and components denpend on this component 62 | # list(APPEND ADD_DEFINITIONS -DAAAAA222=1 63 | # -DAAAAA333=1) 64 | ############################################### 65 | 66 | ############ Add static libs ################## 67 | #### Update parent's variables like CMAKE_C_LINK_FLAGS 68 | # set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--start-group libmaix/libtest.a -ltest2 -Wl,--end-group" PARENT_SCOPE) 69 | ############################################### 70 | 71 | # register component, DYNAMIC or SHARED flags will make component compiled to dynamic(shared) lib 72 | if(CONFIG_COMPONENT3_DYNAMIC) 73 | register_component(DYNAMIC) 74 | else() 75 | register_component() 76 | endif() 77 | endif() 78 | 79 | -------------------------------------------------------------------------------- /components/component3/Kconfig: -------------------------------------------------------------------------------- 1 | 2 | menuconfig COMPONENT3_ENABLED 3 | bool "Enable component3" 4 | default y 5 | 6 | config COMPONENT3_DYNAMIC 7 | bool "compile component as dynamic(shared) lib" 8 | default y 9 | depends on COMPONENT3_ENABLED 10 | 11 | 12 | -------------------------------------------------------------------------------- /components/component3/include/lib3.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIB3_H 2 | #define __LIB3_H 3 | 4 | void lib3_test(); 5 | 6 | #endif 7 | 8 | -------------------------------------------------------------------------------- /components/component3/include_private/lib3_private.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | -------------------------------------------------------------------------------- /components/component3/src/lib3.c: -------------------------------------------------------------------------------- 1 | 2 | #include "lib1.h" 3 | #include "lib3.h" 4 | #include "stdio.h" 5 | #include "global_config.h" 6 | 7 | void lib3_test() 8 | { 9 | printf("hello lib3\n"); 10 | test1(); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /examples/demo1/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | 3 | build 4 | .config.mk 5 | .flash.conf.json 6 | 7 | -------------------------------------------------------------------------------- /examples/demo1/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7.2) 2 | 3 | set(SDK_ENV_NAME "MY_SDK_PATH") 4 | set(CUSTOM_COMPONENTS_PATH_ENV_NAME "CUSTOM_COMPONENTS_PATH") 5 | 6 | set(SDK_PATH_ENV $ENV{${SDK_ENV_NAME}}) 7 | set(CUSTOM_COMPONENTS_PATH_ENV $ENV{${CUSTOM_COMPONENTS_PATH_ENV_NAME}}) 8 | 9 | # Get SDK path 10 | if(NOT SDK_PATH) 11 | get_filename_component(SDK_PATH ../../ ABSOLUTE) 12 | if(SDK_PATH_ENV) 13 | if(EXISTS ${SDK_PATH_ENV}) 14 | set(SDK_PATH ${SDK_PATH_ENV}) 15 | else() 16 | message(FATAL_ERROR "Env variable '${SDK_ENV_NAME}' set, but '${SDK_PATH_ENV}', path not exists") 17 | endif() 18 | endif() 19 | endif() 20 | if(NOT CUSTOM_COMPONENTS_PATH) 21 | if(CUSTOM_COMPONENTS_PATH_ENV) 22 | if(EXISTS ${CUSTOM_COMPONENTS_PATH_ENV}) 23 | set(CUSTOM_COMPONENTS_PATH ${CUSTOM_COMPONENTS_PATH_ENV}) 24 | else() 25 | message(FATAL_ERROR "Env variable '${CUSTOM_COMPONENTS_PATH_ENV_NAME}' set, but '${CUSTOM_COMPONENTS_PATH_ENV}', path not exists") 26 | endif() 27 | endif() 28 | endif() 29 | 30 | # Check SDK Path 31 | if(NOT EXISTS ${SDK_PATH}) 32 | message(FATAL_ERROR "SDK path Error, Please set SDK_PATH or ${SDK_ENV_NAME} variable") 33 | endif() 34 | 35 | # Get Toolchain path 36 | if(NOT CONFIG_TOOLCHAIN_PATH) 37 | if(EXISTS $ENV{MY_TOOLCHAIN_PATH}) 38 | set(CONFIG_TOOLCHAIN_PATH $ENV{MY_TOOLCHAIN_PATH}) 39 | endif() 40 | endif() 41 | 42 | ## Add preprocessor definitions for whole project 43 | # add_definitions(-DAAAAA=1) 44 | 45 | # Call compile 46 | include(${SDK_PATH}/tools/cmake/compile.cmake) 47 | 48 | 49 | # Project Name, default the same as project directory name 50 | get_filename_component(parent_dir ${CMAKE_PARENT_LIST_FILE} DIRECTORY) 51 | get_filename_component(project_dir_name ${parent_dir} NAME) 52 | 53 | set(PROJECT_NAME ${project_dir_name}) # change this var if don't want the same as directory's 54 | 55 | message("-- PROJECT_NAME:${PROJECT_NAME}") 56 | project(${PROJECT_NAME}) 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /examples/demo1/compile/priority.conf: -------------------------------------------------------------------------------- 1 | # component register priority 2 | # The upper components have higher priority 3 | # comments start with `#` 4 | 5 | component1 6 | component2 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/demo1/config_defaults.mk: -------------------------------------------------------------------------------- 1 | 2 | CONFIG_COMPONENT2_ENABLED=y 3 | 4 | -------------------------------------------------------------------------------- /examples/demo1/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ############### Add include ################### 2 | list(APPEND ADD_INCLUDE "include" 3 | ) 4 | list(APPEND ADD_PRIVATE_INCLUDE "") 5 | ############################################### 6 | 7 | ############ Add source files ################# 8 | # list(APPEND ADD_SRCS "src/main.c" 9 | # "src/test.c" 10 | # ) 11 | append_srcs_dir(ADD_SRCS "src") # append source file in src dir to var ADD_SRCS 12 | # list(REMOVE_ITEM COMPONENT_SRCS "src/test2.c") 13 | # FILE(GLOB_RECURSE EXTRA_SRC "src/*.c") 14 | # FILE(GLOB EXTRA_SRC "src/*.c") 15 | # list(APPEND ADD_SRCS ${EXTRA_SRC}) 16 | # aux_source_directory(src ADD_SRCS) # collect all source file in src dir, will set var ADD_SRCS 17 | # append_srcs_dir(ADD_SRCS "src") # append source file in src dir to var ADD_SRCS 18 | # list(REMOVE_ITEM COMPONENT_SRCS "src/test.c") 19 | # set(ADD_ASM_SRCS "src/asm.S") 20 | # list(APPEND ADD_SRCS ${ADD_ASM_SRCS}) 21 | # SET_PROPERTY(SOURCE ${ADD_ASM_SRCS} PROPERTY LANGUAGE C) # set .S ASM file as C language 22 | # SET_SOURCE_FILES_PROPERTIES(${ADD_ASM_SRCS} PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp -D BBBBB") 23 | ############################################### 24 | 25 | ###### Add required/dependent components ###### 26 | list(APPEND ADD_REQUIREMENTS component1) 27 | if(CONFIG_COMPONENT2_ENABLED) 28 | list(APPEND ADD_REQUIREMENTS component2) 29 | endif() 30 | if(CONFIG_COMPONENT3_ENABLED) 31 | list(APPEND ADD_REQUIREMENTS component3) 32 | endif() 33 | # 34 | # list(APPEND ADD_FILE_DEPENDS include/axx.h) 35 | # set_property(SOURCE ${python_h_path} PROPERTY GENERATED 1) 36 | # add_custom_command(OUTPUT include/axx.h 37 | # COMMAND echo "" > include/axx.h 38 | # COMMENT "Generating axx.h ..." 39 | # ) 40 | ############################################### 41 | 42 | ###### Add link search path for requirements/libs ###### 43 | # list(APPEND ADD_LINK_SEARCH_PATH "${CONFIG_TOOLCHAIN_PATH}/lib") 44 | # list(APPEND ADD_REQUIREMENTS pthread m) # add system libs, pthread and math lib for example here 45 | # set (OpenCV_DIR opencv/lib/cmake/opencv4) 46 | # find_package(OpenCV REQUIRED) 47 | ############################################### 48 | 49 | ############ Add static libs ################## 50 | # list(APPEND ADD_STATIC_LIB "lib/libtest.a") 51 | ############################################### 52 | 53 | ############ Add dynamic libs ################## 54 | # list(APPEND ADD_DYNAMIC_LIB "lib/arch/v831/libmaix_nn.so" 55 | # "lib/arch/v831/libmaix_cam.so" 56 | # ) 57 | ############################################### 58 | 59 | #### Add compile option for this component #### 60 | #### Just for this component, won't affect other 61 | #### modules, including component that depend 62 | #### on this component 63 | # list(APPEND ADD_DEFINITIONS_PRIVATE -DAAAAA=1) 64 | 65 | #### Add compile option for this component 66 | #### and components denpend on this component 67 | # list(APPEND ADD_DEFINITIONS -DAAAAA222=1 68 | # -DAAAAA333=1) 69 | ############################################### 70 | 71 | ############ Add static libs ################## 72 | #### Update parent's variables like CMAKE_C_LINK_FLAGS 73 | # set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--start-group libmaix/libtest.a -ltest2 -Wl,--end-group" PARENT_SCOPE) 74 | ############################################### 75 | 76 | 77 | 78 | register_component() 79 | 80 | -------------------------------------------------------------------------------- /examples/demo1/main/Kconfig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neutree/c_cpp_project_framework/435427099998a90fa61e27af48a6565da82a29fe/examples/demo1/main/Kconfig -------------------------------------------------------------------------------- /examples/demo1/main/include/test.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void test(); 4 | 5 | -------------------------------------------------------------------------------- /examples/demo1/main/src/main.c: -------------------------------------------------------------------------------- 1 | 2 | #include "stdio.h" 3 | #include "test.h" 4 | #include "global_config.h" 5 | #include "global_build_info_time.h" 6 | #include "global_build_info_version.h" 7 | 8 | #include "lib1.h" 9 | 10 | #if CONFIG_COMPONENT2_ENABLED 11 | #include "lib2.h" 12 | #endif 13 | 14 | #if CONFIG_COMPONENT3_ENABLED 15 | #include "lib3.h" 16 | #endif 17 | 18 | 19 | // #include "lib2_private.h" // We can't include lib2_private.h for it's compoent2's private include dir 20 | 21 | 22 | int main() 23 | { 24 | #if DEBUG 25 | printf("!!! DEBUG !!!\n"); 26 | #elif RELEASE 27 | printf("!!! RELEASE !!!\n"); 28 | #endif 29 | test(); 30 | test1(); 31 | #if CONFIG_COMPONENT2_ENABLED 32 | lib2_test(); 33 | #else 34 | printf("lib2 disabled\r\n"); 35 | #endif 36 | 37 | #if AAAAA 38 | printf("AAAAAA from main\n"); 39 | #endif 40 | 41 | #if AAAAA222 42 | printf("AAAAAA222 from main\n"); 43 | #endif 44 | 45 | #if CONFIG_COMPONENT3_ENABLED 46 | lib3_test(); 47 | #endif 48 | 49 | return 0; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /examples/demo1/main/src/test.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include "stdio.h" 3 | 4 | void test() 5 | { 6 | printf("test\r\n"); 7 | } 8 | 9 | -------------------------------------------------------------------------------- /examples/demo1/main/src/test2.c: -------------------------------------------------------------------------------- 1 | 2 | #include "stdio.h" 3 | 4 | void test2() 5 | { 6 | printf("test2\r\n"); 7 | } 8 | 9 | -------------------------------------------------------------------------------- /examples/demo1/project.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #-*- coding = utf-8 -*- 3 | 4 | # 5 | # @file from https://github.com/Neutree/c_cpp_project_framework 6 | # @author neucrack 7 | # @license Apache 2.0 8 | # 9 | 10 | import sys, os 11 | 12 | sdk_env_name = "MY_SDK_PATH" 13 | custom_component_path_name = "CUSTOM_COMPONENTS_PATH" 14 | 15 | # get SDK absolute path 16 | sdk_path = os.path.abspath(sys.path[0]+"/../../") 17 | try: 18 | if os.environ[sdk_env_name] and os.path.exists(os.environ[sdk_env_name]): 19 | sdk_path = os.environ[sdk_env_name] 20 | except Exception: 21 | pass 22 | print("-- SDK_PATH:{}".format(sdk_path)) 23 | 24 | # get custom components path 25 | custom_components_path = None 26 | try: 27 | if os.environ[custom_component_path_name] and os.path.exists(os.environ[custom_component_path_name]): 28 | custom_components_path = os.environ[custom_component_path_name] 29 | except Exception: 30 | pass 31 | print("-- CUSTOM_COMPONENTS_PATH:{}".format(custom_components_path)) 32 | 33 | # execute project script from SDK 34 | project_file_path = sdk_path+"/tools/cmake/project.py" 35 | with open(project_file_path) as f: 36 | exec(f.read()) 37 | 38 | -------------------------------------------------------------------------------- /tools/cmake/compile.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # @file from https://github.com/Neutree/c_cpp_project_framework 3 | # @author neucrack 4 | # @license Apache 2.0 5 | # 6 | 7 | 8 | # Convert to cmake path(for Windows) 9 | file(TO_CMAKE_PATH "${SDK_PATH}" SDK_PATH) 10 | 11 | get_filename_component(parent_dir ${CMAKE_PARENT_LIST_FILE} DIRECTORY) 12 | get_filename_component(current_dir ${CMAKE_CURRENT_LIST_FILE} DIRECTORY) 13 | get_filename_component(parent_dir_name ${parent_dir} NAME) 14 | 15 | # global variables 16 | set(g_dynamic_libs "" CACHE INTERNAL "g_dynamic_libs") 17 | set(g_link_search_path "" CACHE INTERNAL "g_link_search_path") 18 | 19 | # Set project dir, so just projec can include this cmake file!!! 20 | set(PROJECT_SOURCE_DIR ${parent_dir}) 21 | set(PROJECT_PATH ${PROJECT_SOURCE_DIR}) 22 | set(PROJECT_BINARY_DIR "${parent_dir}/build") 23 | set(PROJECT_DIST_DIR "${parent_dir}/dist") 24 | message(STATUS "SDK_PATH:${SDK_PATH}") 25 | message(STATUS "PROJECT_PATH:${PROJECT_SOURCE_DIR}") 26 | 27 | include(${SDK_PATH}/tools/cmake/tools.cmake) 28 | 29 | function(register_component) 30 | get_filename_component(component_dir ${CMAKE_CURRENT_LIST_FILE} DIRECTORY) 31 | get_filename_component(component_name ${component_dir} NAME) 32 | message(STATUS "[register component: ${component_name} ], path:${component_dir}") 33 | 34 | # Get params: DYNAMIC/SHARED 35 | foreach(name ${ARGN}) 36 | string(TOUPPER ${name} name) 37 | if(${name} STREQUAL "DYNAMIC" OR ${name} STREQUAL "SHARED") 38 | set(to_dynamic_lib true) 39 | endif() 40 | endforeach() 41 | if(to_dynamic_lib) 42 | message("-- component ${component_name} will compiled to dynamic lib") 43 | # Add dynamic file path to g_dynamic_libs variable 44 | set(dynamic_libs ${g_dynamic_libs}) 45 | list(APPEND dynamic_libs "${PROJECT_BINARY_DIR}/${component_name}/lib${component_name}${DL_EXT}") 46 | set(g_dynamic_libs ${dynamic_libs} CACHE INTERNAL "g_dynamic_libs") 47 | else() 48 | message("-- component ${component_name} will compiled to static lib") 49 | endif() 50 | 51 | # Add src to lib 52 | if(ADD_SRCS) 53 | if(to_dynamic_lib) 54 | add_library(${component_name} SHARED ${ADD_SRCS}) 55 | else() 56 | add_library(${component_name} STATIC ${ADD_SRCS}) 57 | endif() 58 | set(include_type PUBLIC) 59 | else() 60 | if(to_dynamic_lib) 61 | add_library(${component_name} SHARED) 62 | set(include_type PUBLIC) 63 | else() 64 | add_library(${component_name} INTERFACE) 65 | set(include_type INTERFACE) 66 | endif() 67 | endif() 68 | 69 | # Add include 70 | foreach(include_dir ${ADD_INCLUDE}) 71 | get_filename_component(abs_dir ${include_dir} ABSOLUTE BASE_DIR ${component_dir}) 72 | if(NOT IS_DIRECTORY ${abs_dir}) 73 | message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: ${include_dir} not found!") 74 | endif() 75 | target_include_directories(${component_name} ${include_type} ${abs_dir}) 76 | endforeach() 77 | 78 | # Add private include 79 | foreach(include_dir ${ADD_PRIVATE_INCLUDE}) 80 | if(${include_type} STREQUAL INTERFACE) 81 | message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: ADD_PRIVATE_INCLUDE set but no source file!") 82 | endif() 83 | get_filename_component(abs_dir ${include_dir} ABSOLUTE BASE_DIR ${component_dir}) 84 | if(NOT IS_DIRECTORY ${abs_dir}) 85 | message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: ${include_dir} not found!") 86 | endif() 87 | target_include_directories(${component_name} PRIVATE ${abs_dir}) 88 | endforeach() 89 | 90 | # Add blobal config include 91 | if(${include_type} STREQUAL INTERFACE) 92 | target_include_directories(${component_name} INTERFACE ${global_config_dir}) 93 | else() 94 | target_include_directories(${component_name} PUBLIC ${global_config_dir}) 95 | endif() 96 | 97 | # Add definitions public 98 | foreach(difinition ${ADD_DEFINITIONS}) 99 | if(${include_type} STREQUAL INTERFACE) 100 | target_compile_options(${component_name} INTERFACE ${difinition}) 101 | target_link_options(${component_name} INTERFACE ${difinition}) 102 | else() 103 | target_compile_options(${component_name} PUBLIC ${difinition}) 104 | target_link_options(${component_name} PUBLIC ${difinition}) 105 | endif() 106 | endforeach() 107 | 108 | # Add definitions private 109 | foreach(difinition ${ADD_DEFINITIONS_PRIVATE}) 110 | target_compile_options(${component_name} PRIVATE ${difinition}) 111 | target_link_options(${component_name} PRIVATE ${difinition}) 112 | endforeach() 113 | 114 | # Add lib search path 115 | if(ADD_LINK_SEARCH_PATH) 116 | foreach(path ${ADD_LINK_SEARCH_PATH}) 117 | if(NOT EXISTS "${path}") 118 | prepend(lib_full "${component_dir}/" ${path}) 119 | if(NOT EXISTS "${lib_full}") 120 | message(FATAL_ERROR "Can not find ${path} or ${lib_full}") 121 | endif() 122 | set(path ${lib_full}) 123 | endif() 124 | get_filename_component(abs_dir ${path} ABSOLUTE) 125 | if(EXISTS "${abs_dir}") 126 | set(link_search_path ${g_link_search_path}) 127 | list(APPEND link_search_path "${abs_dir}") 128 | # target_link_directories(${component_name} PUBLIC ${link_search_path}) # this will fail add -L -Wl,-rpath flag for some .so 129 | list(REMOVE_DUPLICATES link_search_path) 130 | set(g_link_search_path ${link_search_path} CACHE INTERNAL "g_link_search_path") 131 | endif() 132 | endforeach() 133 | endif() 134 | 135 | # Add static lib 136 | if(ADD_STATIC_LIB) 137 | foreach(lib ${ADD_STATIC_LIB}) 138 | if(NOT EXISTS "${lib}") 139 | prepend(lib_full "${component_dir}/" ${lib}) 140 | if(NOT EXISTS "${lib_full}") 141 | message(FATAL_ERROR "Can not find ${lib} or ${lib_full}") 142 | endif() 143 | set(lib ${lib_full}) 144 | endif() 145 | target_link_libraries(${component_name} ${include_type} ${lib}) 146 | endforeach() 147 | endif() 148 | # Add dynamic lib 149 | if(ADD_DYNAMIC_LIB) 150 | set(dynamic_libs ${g_dynamic_libs}) 151 | foreach(lib ${ADD_DYNAMIC_LIB}) 152 | if(NOT EXISTS "${lib}") 153 | prepend(lib_full "${component_dir}/" ${lib}) 154 | if(NOT EXISTS "${lib_full}") 155 | message(FATAL_ERROR "Can not find ${lib} or ${lib_full}") 156 | endif() 157 | set(lib ${lib_full}) 158 | endif() 159 | get_filename_component(lib ${lib} ABSOLUTE) 160 | list(APPEND dynamic_libs ${lib}) 161 | get_filename_component(lib_dir ${lib} DIRECTORY) 162 | get_filename_component(lib_name ${lib} NAME) 163 | target_link_libraries(${component_name} ${include_type} -L${lib_dir} ${lib_name}) 164 | endforeach() 165 | list(REMOVE_DUPLICATES dynamic_libs) 166 | set(g_dynamic_libs ${dynamic_libs} CACHE INTERNAL "g_dynamic_libs") 167 | endif() 168 | 169 | # Add requirements 170 | target_link_libraries(${component_name} ${include_type} ${ADD_REQUIREMENTS}) 171 | 172 | # Add file depends 173 | if(ADD_FILE_DEPENDS) 174 | add_custom_target(${component_name}_file_depends DEPENDS ${ADD_FILE_DEPENDS}) 175 | add_dependencies(${component_name} ${component_name}_file_depends) 176 | endif() 177 | endfunction() 178 | 179 | function(is_path_component ret param_path) 180 | get_filename_component(abs_dir ${param_path} ABSOLUTE) 181 | 182 | if(NOT IS_DIRECTORY "${abs_dir}") 183 | set(${ret} 0 PARENT_SCOPE) 184 | return() 185 | endif() 186 | 187 | get_filename_component(base_dir ${abs_dir} NAME) 188 | string(SUBSTRING "${base_dir}" 0 1 first_char) 189 | 190 | if(first_char STREQUAL ".") 191 | set(${ret} 0 PARENT_SCOPE) 192 | return() 193 | endif() 194 | if(NOT EXISTS "${abs_dir}/CMakeLists.txt") 195 | set(${ret} 0 PARENT_SCOPE) 196 | return() 197 | endif() 198 | 199 | # check if register_component in CMakeLists.txt 200 | file(READ "${abs_dir}/CMakeLists.txt" content) 201 | string(FIND "${content}" "register_component" find_res) 202 | if(find_res EQUAL -1) 203 | set(${ret} 0 PARENT_SCOPE) 204 | return() 205 | endif() 206 | 207 | set(${ret} 1 PARENT_SCOPE) 208 | endfunction() 209 | 210 | function(find_components componet_dirs kconfigs configs found_main find_dir) 211 | set(_componet_dirs ${${componet_dirs}}) 212 | set(_kconfigs ${${configs}}) 213 | set(_configs ${${configs}}) 214 | set(_found_main ${${found_main}}) 215 | file(GLOB component_dirs ${find_dir}) 216 | foreach(component_dir ${component_dirs}) 217 | is_path_component(is_component ${component_dir}) 218 | if(is_component) 219 | message(STATUS "Find component: ${component_dir}") 220 | get_filename_component(base_dir ${component_dir} NAME) 221 | if(${base_dir} STREQUAL "main") 222 | set(_found_main 1) 223 | endif() 224 | list(APPEND _componet_dirs ${component_dir}) 225 | if(EXISTS ${component_dir}/Kconfig) 226 | message(STATUS "Find component Kconfig of ${base_dir}") 227 | list(APPEND _kconfigs ${component_dir}/Kconfig PARENT_SCOPE) 228 | endif() 229 | if(EXISTS ${component_dir}/config_defaults.mk) 230 | message(STATUS "Find component defaults config of ${base_dir}") 231 | list(APPEND _configs --defaults "${component_dir}/config_defaults.mk" PARENT_SCOPE) 232 | endif() 233 | endif() 234 | endforeach() 235 | set(${componet_dirs} ${_componet_dirs} PARENT_SCOPE) 236 | set(${kconfigs} ${_kconfigs} PARENT_SCOPE) 237 | set(${configs} ${_configs} PARENT_SCOPE) 238 | set(${found_main} ${_found_main} PARENT_SCOPE) 239 | endfunction() 240 | 241 | function(get_python python version info_str) 242 | set(res 1) 243 | execute_process(COMMAND python3 --version RESULT_VARIABLE cmd_res OUTPUT_VARIABLE cmd_out) 244 | if(${cmd_res} EQUAL 0) 245 | set(${python} python3 PARENT_SCOPE) 246 | set(${version} 3 PARENT_SCOPE) 247 | set(${info_str} ${cmd_out} PARENT_SCOPE) 248 | else() 249 | execute_process(COMMAND python --version RESULT_VARIABLE cmd_res OUTPUT_VARIABLE cmd_out) 250 | if(${cmd_res} EQUAL 0) 251 | set(${python} python PARENT_SCOPE) 252 | set(${version} 2 PARENT_SCOPE) 253 | set(${info_str} ${cmd_out} PARENT_SCOPE) 254 | endif() 255 | endif() 256 | endfunction(get_python python) 257 | 258 | 259 | macro(project name) 260 | get_filename_component(current_dir ${CMAKE_CURRENT_LIST_FILE} DIRECTORY) 261 | set(PROJECT_SOURCE_DIR ${current_dir}) 262 | set(PROJECT_BINARY_DIR "${current_dir}/build") 263 | 264 | # Find components in SDK's components folder, register components 265 | find_components(components_dirs components_kconfig_files kconfig_defaults_files_args found_main ${SDK_PATH}/components/*) 266 | # Find components in custom components folder, register components 267 | if(CUSTOM_COMPONENTS_PATH) 268 | find_components(components_dirs components_kconfig_files kconfig_defaults_files_args found_main ${CUSTOM_COMPONENTS_PATH}/*) 269 | endif() 270 | # Find components in projects' shared components folder, register components 271 | find_components(components_dirs components_kconfig_files kconfig_defaults_files_args found_main ${PROJECT_SOURCE_DIR}/../components/*) 272 | # Find components in project folder 273 | find_components(components_dirs components_kconfig_files kconfig_defaults_files_args found_main ${PROJECT_SOURCE_DIR}/*) 274 | find_components(components_dirs components_kconfig_files kconfig_defaults_files_args found_main ${PROJECT_SOURCE_DIR}/components/*) 275 | 276 | if(NOT found_main) 277 | message(FATAL_ERROR "=================\nCan not find main component(folder) in project folder!!\n=================") 278 | endif() 279 | 280 | # Find default config file 281 | if(DEFAULT_CONFIG_FILE) 282 | message(STATUS "Project defaults config file:${DEFAULT_CONFIG_FILE}") 283 | list(APPEND kconfig_defaults_files_args --defaults "${DEFAULT_CONFIG_FILE}") 284 | if(EXISTS ${PROJECT_SOURCE_DIR}/.config.mk) 285 | message(STATUS "Find project defaults config(.config.mk)") 286 | list(APPEND kconfig_defaults_files_args --defaults "${PROJECT_SOURCE_DIR}/.config.mk") 287 | endif() 288 | else() 289 | if(EXISTS ${PROJECT_SOURCE_DIR}/config_defaults.mk) 290 | message(STATUS "Find project defaults config(config_defaults.mk)") 291 | list(APPEND kconfig_defaults_files_args --defaults "${PROJECT_SOURCE_DIR}/config_defaults.mk") 292 | endif() 293 | if(EXISTS ${PROJECT_SOURCE_DIR}/.config.mk) 294 | message(STATUS "Find project defaults config(.config.mk)") 295 | list(APPEND kconfig_defaults_files_args --defaults "${PROJECT_SOURCE_DIR}/.config.mk") 296 | endif() 297 | endif() 298 | 299 | # Generate config file from Kconfig 300 | get_python(python python_version python_info_str) 301 | if(NOT python) 302 | message(FATAL_ERROR "python not found, please install python firstly(python3 recommend)!") 303 | endif() 304 | message(STATUS "python command: ${python}, version: ${python_info_str}") 305 | string(REPLACE ";" " " components_kconfig_files "${kconfig_defaults_files_args}") 306 | string(REPLACE ";" " " components_kconfig_files "${components_kconfig_files}") 307 | set(generate_config_cmd ${python} -u ${SDK_PATH}/tools/kconfig/genconfig.py 308 | --kconfig "${SDK_PATH}/Kconfig" 309 | ${kconfig_defaults_files_args} 310 | --menuconfig False 311 | --env "SDK_PATH=${SDK_PATH}" 312 | --env "PROJECT_PATH=${PROJECT_SOURCE_DIR}" 313 | --env "CUSTOM_COMPONENTS_PATH=${CUSTOM_COMPONENTS_PATH}" 314 | --env "BUILD_TYPE=${CMAKE_BUILD_TYPE}" 315 | --output makefile ${PROJECT_BINARY_DIR}/config/global_config.mk 316 | --output cmake ${PROJECT_BINARY_DIR}/config/global_config.cmake 317 | --output header ${PROJECT_BINARY_DIR}/config/global_config.h 318 | ) 319 | set(generate_config_cmd2 ${python} -u ${SDK_PATH}/tools/kconfig/genconfig.py 320 | --kconfig "${SDK_PATH}/Kconfig" 321 | ${kconfig_defaults_files_args} 322 | --menuconfig True 323 | --env "SDK_PATH=${SDK_PATH}" 324 | --env "PROJECT_PATH=${PROJECT_SOURCE_DIR}" 325 | --env "CUSTOM_COMPONENTS_PATH=${CUSTOM_COMPONENTS_PATH}" 326 | --env "BUILD_TYPE=${CMAKE_BUILD_TYPE}" 327 | --output makefile ${PROJECT_BINARY_DIR}/config/global_config.mk 328 | --output cmake ${PROJECT_BINARY_DIR}/config/global_config.cmake 329 | --output header ${PROJECT_BINARY_DIR}/config/global_config.h 330 | ) 331 | execute_process(COMMAND ${generate_config_cmd} RESULT_VARIABLE cmd_res) 332 | if(NOT cmd_res EQUAL 0) 333 | message(FATAL_ERROR "Check Kconfig content") 334 | endif() 335 | 336 | # Include confiurations 337 | set(global_config_dir "${PROJECT_BINARY_DIR}/config") 338 | include(${global_config_dir}/global_config.cmake) 339 | if(WIN32) 340 | set(EXT ".exe") 341 | set(DL_EXT ".dll") 342 | else() 343 | set(EXT "") 344 | set(DL_EXT ".so") 345 | endif() 346 | 347 | # Config toolchain 348 | if(CONFIG_TOOLCHAIN_PATH MATCHES ".*.cmake$" AND NOT CMAKE_TOOLCHAIN_FILE) 349 | message("-- CONFIG_TOOLCHAIN_PATH is cmake file: ${CONFIG_TOOLCHAIN_PATH}") 350 | set(CMAKE_TOOLCHAIN_FILE ${CONFIG_TOOLCHAIN_PATH}) 351 | endif() 352 | if(CMAKE_TOOLCHAIN_FILE) 353 | message("-- CMAKE_TOOLCHAIN_FILE set: ${CMAKE_TOOLCHAIN_FILE}") 354 | else() 355 | if(CONFIG_TOOLCHAIN_PATH OR CONFIG_TOOLCHAIN_PREFIX) 356 | if(CONFIG_TOOLCHAIN_PATH) 357 | if(WIN32) 358 | file(TO_CMAKE_PATH ${CONFIG_TOOLCHAIN_PATH} CONFIG_TOOLCHAIN_PATH) 359 | endif() 360 | if(NOT IS_DIRECTORY ${CONFIG_TOOLCHAIN_PATH}) 361 | message(FATAL_ERROR "TOOLCHAIN_PATH set error:${CONFIG_TOOLCHAIN_PATH}") 362 | endif() 363 | set(path_split /) 364 | set(TOOLCHAIN_PATH ${CONFIG_TOOLCHAIN_PATH}) 365 | message(STATUS "TOOLCHAIN_PATH:${CONFIG_TOOLCHAIN_PATH}") 366 | set(CMAKE_C_COMPILER "${CONFIG_TOOLCHAIN_PATH}${path_split}${CONFIG_TOOLCHAIN_PREFIX}gcc${EXT}") 367 | set(CMAKE_CXX_COMPILER "${CONFIG_TOOLCHAIN_PATH}${path_split}${CONFIG_TOOLCHAIN_PREFIX}g++${EXT}") 368 | set(CMAKE_ASM_COMPILER "${CONFIG_TOOLCHAIN_PATH}${path_split}${CONFIG_TOOLCHAIN_PREFIX}gcc${EXT}") 369 | set(CMAKE_LINKER "${CONFIG_TOOLCHAIN_PATH}${path_split}${CONFIG_TOOLCHAIN_PREFIX}ld${EXT}") 370 | else() 371 | message(STATUS "No TOOLCHAIN_PATH, only set TOOLCHAIN_PREFIX: ${CONFIG_TOOLCHAIN_PREFIX}") 372 | set(CMAKE_C_COMPILER "${CONFIG_TOOLCHAIN_PREFIX}gcc${EXT}") 373 | set(CMAKE_CXX_COMPILER "${CONFIG_TOOLCHAIN_PREFIX}g++${EXT}") 374 | set(CMAKE_ASM_COMPILER "${CONFIG_TOOLCHAIN_PREFIX}gcc${EXT}") 375 | set(CMAKE_LINKER "${CONFIG_TOOLCHAIN_PREFIX}ld${EXT}") 376 | endif() 377 | else() 378 | message(STATUS "use default toolchain: gcc") 379 | set(CMAKE_C_COMPILER "gcc${EXT}") 380 | set(CMAKE_CXX_COMPILER "g++${EXT}") 381 | set(CMAKE_ASM_COMPILER "gcc${EXT}") 382 | set(CMAKE_LINKER "ld${EXT}") 383 | endif() 384 | endif() 385 | 386 | set(CMAKE_C_COMPILER_WORKS 1) 387 | set(CMAKE_CXX_COMPILER_WORKS 1) 388 | 389 | 390 | # set(CMAKE_SYSTEM_NAME Generic) # set this flag may leads to dymamic(/shared) lib compile fail 391 | 392 | # Declare project # This function will cler flags! 393 | _project(${name} ASM C CXX) 394 | 395 | if(CMAKE_EXECUTABLE_SUFFIX STREQUAL ".js") # generate js ang html file for WASM 396 | set(CMAKE_EXECUTABLE_SUFFIX ".html") 397 | endif() 398 | 399 | if(EXISTS "${PROJECT_PATH}/compile/compile_flags.cmake") 400 | include("${PROJECT_PATH}/compile/compile_flags.cmake") 401 | else() 402 | include("${SDK_PATH}/tools/cmake/compile_flags.cmake") 403 | endif() 404 | 405 | # add DEBUG or RELEASE flag globally 406 | if(NOT CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "Debug") 407 | add_definitions(-DDEBUG=1 -DRELEASE=0) 408 | # message("!!! DEBUG !!!") 409 | else() 410 | add_definitions(-DRELEASE=1 -DDEBUG=0) 411 | # message("!!! RELEASE !!!") 412 | endif() 413 | 414 | # Add dependence: update configfile, append time and git info for global config header file 415 | # we didn't generate build info for cmake and makefile for if we do, it will always rebuild cmake 416 | # everytime we execute make 417 | set(gen_build_info_config_cmd ${python} ${SDK_PATH}/tools/kconfig/update_build_info.py 418 | --configfile header ${PROJECT_BINARY_DIR}/config/global_build_info_time.h ${PROJECT_BINARY_DIR}/config/global_build_info_version.h 419 | ) 420 | add_custom_target(update_build_info COMMAND ${gen_build_info_config_cmd}) 421 | 422 | # Sort component according to priority.conf config file 423 | set(component_priority_conf_file "${PROJECT_PATH}/compile/priority.conf") 424 | set(sort_components ${python} ${SDK_PATH}/tools/cmake/sort_components.py 425 | ${component_priority_conf_file} ${components_dirs} 426 | ) 427 | execute_process(COMMAND ${sort_components} OUTPUT_VARIABLE component_dirs_sorted RESULT_VARIABLE cmd_res) 428 | if(cmd_res EQUAL 2) 429 | message(STATUS "No components priority config file") 430 | set(component_dirs_sorted ${components_dirs}) 431 | elseif(cmd_res EQUAL 0) 432 | message(STATUS "Config components priority success") 433 | else() 434 | message(STATUS "Components priority config fail ${component_dirs_sorted}, check config file:${component_priority_conf_file}") 435 | endif() 436 | 437 | # Call CMakeLists.txt 438 | foreach(component_dir ${component_dirs_sorted}) 439 | get_filename_component(base_dir ${component_dir} NAME) 440 | add_subdirectory(${component_dir} ${base_dir} EXCLUDE_FROM_ALL) 441 | if(TARGET ${base_dir}) 442 | add_dependencies(${base_dir} update_build_info) # add build info dependence 443 | else() 444 | message(STATUS "component ${base_dir} not enabled") 445 | endif() 446 | endforeach() 447 | 448 | # Add lib search path to link flags 449 | foreach(abs_dir ${g_link_search_path}) 450 | set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -L${abs_dir} -Wl,-rpath,${abs_dir}") 451 | set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -L${abs_dir} -Wl,-rpath,${abs_dir}") 452 | endforeach() 453 | 454 | # Add menuconfig target for makefile 455 | add_custom_target(menuconfig COMMAND ${generate_config_cmd2}) 456 | 457 | # Create dummy source file exe_src.c to satisfy cmake's `add_executable` interface! 458 | set(exe_src ${CMAKE_BINARY_DIR}/exe_src.c) 459 | add_executable(${name} "${exe_src}") 460 | add_custom_command(OUTPUT ${exe_src} COMMAND ${CMAKE_COMMAND} -E touch ${exe_src} VERBATIM) 461 | add_custom_target(gen_exe_src DEPENDS "${exe_src}") 462 | add_dependencies(${name} gen_exe_src) 463 | 464 | # Add main component(lib) 465 | target_link_libraries(${name} main) 466 | 467 | # Add binary 468 | if(EXISTS "${PROJECT_PATH}/compile/gen_binary.cmake") 469 | include("${PROJECT_PATH}/compile/gen_binary.cmake") 470 | else() 471 | include("${SDK_PATH}/tools/cmake/gen_binary.cmake") 472 | endif() 473 | 474 | endmacro() 475 | 476 | 477 | 478 | -------------------------------------------------------------------------------- /tools/cmake/compile_flags.cmake: -------------------------------------------------------------------------------- 1 | 2 | ########## set C flags ######### 3 | set(CMAKE_C_FLAGS -Wall) 4 | ################################ 5 | 6 | 7 | ###### set CXX(cpp) flags ###### 8 | set(CMAKE_CXX_FLAGS -Wall) 9 | ################################ 10 | 11 | # set(LINK_FLAGS -Wl,-EL) # (default little edian) 12 | set(CMAKE_C_LINK_FLAGS ${CMAKE_C_LINK_FLAGS} 13 | ${LINK_FLAGS} 14 | ) 15 | set(CMAKE_CXX_LINK_FLAGS ${CMAKE_C_LINK_FLAGS} 16 | ) 17 | # set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} 18 | # ${LINK_FLAGS} 19 | # ) 20 | # set(CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} 21 | # ${LINK_FLAGS} 22 | # ) 23 | # set(CMAKE_MODULE_LINKER_FLAGS ${CMAKE_MODULE_LINKER_FLAGS} 24 | # ${LINK_FLAGS} 25 | # ) 26 | 27 | 28 | # Convert list to string 29 | string(REPLACE ";" " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") 30 | string(REPLACE ";" " " CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 31 | string(REPLACE ";" " " LINK_FLAGS "${LINK_FLAGS}") 32 | string(REPLACE ";" " " CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS}") 33 | string(REPLACE ";" " " CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS}") 34 | # string(REPLACE ";" " " CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") 35 | # string(REPLACE ";" " " CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") 36 | # string(REPLACE ";" " " CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}") 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /tools/cmake/gen_binary.cmake: -------------------------------------------------------------------------------- 1 | 2 | # set(CMAKE_C_LINK_EXECUTABLE " -o .elf ") 3 | # set(CMAKE_CXX_LINK_EXECUTABLE " -o .elf ") 4 | 5 | # add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 6 | # COMMAND ${CMAKE_OBJCOPY} --output-format=binary ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.elf ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bin 7 | # DEPENDS ${PROJECT_NAME} 8 | # COMMENT "-- Generating binary file ...") 9 | 10 | # variable #{g_dynamic_libs} have dependency dynamic libs and compiled dynamic libs(register component and assigned DYNAMIC or SHARED flag) 11 | 12 | # add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 13 | # COMMAND mkdir -p ${PROJECT_DIST_DIR} 14 | # COMMAND cp ${g_dynamic_libs} ${PROJECT_DIST_DIR} 15 | # COMMAND cp ${PROJECT_BINARY_DIR}/${PROJECT_NAME} ${PROJECT_DIST_DIR} 16 | # DEPENDS ${PROJECT_NAME} 17 | # COMMENT "-- copy binary files to dist dir ...") 18 | 19 | -------------------------------------------------------------------------------- /tools/cmake/project.py: -------------------------------------------------------------------------------- 1 | # 2 | # @file from https://github.com/Neutree/c_cpp_project_framework 3 | # @author neucrack 4 | # @license MIT 5 | # 6 | 7 | 8 | import argparse 9 | import os, sys, time, re, shutil 10 | import subprocess 11 | from multiprocessing import cpu_count 12 | 13 | if not os.path.exists("CMakeLists.txt") or not os.path.exists("main"): 14 | print("please run me at project folder!") 15 | exit(1) 16 | 17 | try: 18 | # sdk_path from project's project.py 19 | sdk_path = sdk_path 20 | except Exception: 21 | sdk_path = os.path.abspath("../../") 22 | project_path = sys.path[0] 23 | project_name = "" 24 | project_cmake_path = project_path+"/CMakeLists.txt" 25 | project_cmake_content = "" 26 | with open(project_cmake_path) as f: 27 | project_cmake_content = f.read() 28 | match = re.findall(r"set\(PROJECT_NAME (.*)\)", project_cmake_content) 29 | if len(match) != 0: 30 | project_name = match[0] 31 | if("project_dir_name" in project_name): 32 | project_name = os.path.basename(project_path) 33 | print("-- project name: {}".format(project_name)) 34 | if project_name == "": 35 | print("[ERROR] Can not find project name in {}, not set(PROJECT_NAME {})".format(project_cmake_path, "${project_name}")) 36 | exit(1) 37 | 38 | # find extra tools 39 | tools_dir = os.path.join(sdk_path, "tools", "cmds") 40 | sys.path.insert(1, tools_dir) 41 | # find all .py files in tools dir 42 | extra_tools_names = [] 43 | parsers = [] 44 | extra_tools = {} 45 | for name in os.listdir(tools_dir): 46 | if name.endswith(".py"): 47 | extra_tools_names.append(name[:-3]) 48 | # import all tools 49 | for name in extra_tools_names: 50 | # import from tool file 51 | tool = __import__(name) 52 | hasattr(tool, "parser") and parsers.append(tool.parser) 53 | if hasattr(tool, "cmds"): 54 | extra_tools[tool.__name__] = { 55 | "tool": tool, 56 | "cmds": tool.cmds, 57 | } 58 | 59 | project_parser = argparse.ArgumentParser(description='build tool, e.g. `python project.py build`', prog="project.py", parents=parsers) 60 | 61 | project_parser.add_argument('--toolchain', 62 | help='toolchain path ( absolute path )', 63 | metavar='PATH', 64 | default="") 65 | 66 | project_parser.add_argument('--toolchain-prefix', 67 | help='toolchain prefix(e.g. mips-elf-', 68 | metavar='PREFIX', 69 | default="") 70 | project_parser.add_argument('--config_file', 71 | help='config file path, e.g. config_defaults.mk', 72 | metavar='PATH', 73 | default="{}/config_defaults.mk".format(project_path)) 74 | project_parser.add_argument('--verbose', 75 | help='for build command, execute `cmake -build . --verbose` to compile', 76 | action="store_true", 77 | default=False) 78 | project_parser.add_argument('-G', '--generator', default="", help="project type to generate, supported type on your platform see `cmake --help`") 79 | project_parser.add_argument('--release', action="store_true", default=False, help="release mode, default is debug mode") 80 | project_parser.add_argument('--build-type', default=None, help="build type, [Debug, Release, MinRelSize, RelWithDebInfo], you can also set build type by CMAKE_BUILD_TYPE environment variable") 81 | 82 | cmd_help =''' 83 | project command: 84 | 85 | config: config toolchain path 86 | clean_conf: clean toolchain path config 87 | menuconfig: open menuconfig pannel, a visual config pannel 88 | build: start compile project, temp files in `build` dir, dist files in `dist` dir 89 | rebuild: update cmakefiles and build, if new file added, shoud use this command 90 | clean: clean build files, won't clean configuration 91 | distclean: clean all build files and configuration except configuration configed by config conmand 92 | flash: burn firmware to board's flash 93 | ''' 94 | 95 | cmd_choices = ["config", "build", "rebuild", "menuconfig", "clean", "distclean", "clean_conf"] 96 | for k, v in extra_tools.items(): 97 | cmd_choices.extend(v["cmds"]) 98 | cmd_choices = list(set(cmd_choices)) 99 | project_parser.add_argument("cmd", 100 | help=cmd_help, 101 | choices=cmd_choices 102 | ) 103 | 104 | project_args = project_parser.parse_args() 105 | 106 | cwd = sys.path[0] 107 | os.chdir(cwd) 108 | 109 | config_filename = ".config.mk" 110 | 111 | def load_config_mk(path): 112 | configs = {} 113 | if os.path.exists(config_filename): 114 | with open(config_filename) as f: 115 | content = f.read() 116 | lines = content.split("\n") 117 | for line in lines: 118 | line = line.strip() 119 | if (not line) or line.startswith("#"): 120 | continue 121 | k, v = line.split("=") 122 | if v.startswith('"') and v.endswith('"'): 123 | v = v[1:-1] 124 | elif v in ["y", "n"]: 125 | v = True if v == "y" else False 126 | else: 127 | try: 128 | if v.startswith("0x"): 129 | v = int(v, 16) 130 | else: 131 | v = int(v) 132 | except: 133 | pass 134 | configs[k] = v 135 | return configs 136 | 137 | def dump_config_mk(configs, path): 138 | with open(config_filename, "w") as f: 139 | for k, v in configs.items(): 140 | if isinstance(v, bool): 141 | v = "y" if v else "n" 142 | elif isinstance(v, int): 143 | v = str(v) 144 | elif isinstance(v, str): 145 | v = '"' + v + '"' 146 | f.write("{}={}\n".format(k, v)) 147 | 148 | def get_config_files(config_file, sdk_path, project_path): 149 | files = [] 150 | config_mk = os.path.join(project_path, ".config.mk") 151 | config_default = os.path.join(project_path, "config_defaults.mk") 152 | if config_file and os.path.exists(config_file): 153 | files.append(config_file) 154 | elif os.path.exists(config_default): 155 | files.append(config_default) 156 | if os.path.exists(config_mk): 157 | files.append(config_mk) 158 | return files 159 | 160 | configs = load_config_mk(config_filename) 161 | configs_old = configs.copy() 162 | 163 | header = "# Generated by config.py, DO NOT edit!\n\n" 164 | config_content = header 165 | if project_args.generator.strip(): 166 | configs["CONFIG_CMAKE_GENERATOR"] = project_args.generator 167 | if project_args.toolchain.strip() != "": 168 | if not os.path.exists(project_args.toolchain): 169 | print("[ERROR] config toolchain path error:", project_args.toolchain) 170 | exit(1) 171 | project_args.toolchain = project_args.toolchain.strip().replace("\\","/") 172 | configs["CONFIG_TOOLCHAIN_PATH"] = project_args.toolchain 173 | if project_args.toolchain_prefix.strip() != "": 174 | project_args.toolchain_prefix = project_args.toolchain_prefix.strip().replace("\\","/") 175 | configs["CONFIG_TOOLCHAIN_PREFIX"] = project_args.toolchain_prefix 176 | if "CONFIG_CMAKE_GENERATOR" not in configs or not configs["CONFIG_CMAKE_GENERATOR"]: 177 | configs["CONFIG_CMAKE_GENERATOR"] = "Unix Makefiles" 178 | print('no generator set, will use "Unix Makefiles" as default') 179 | if configs != configs_old: 180 | dump_config_mk(configs, config_filename) 181 | if os.path.exists("build/config/global_config.mk"): 182 | os.remove("build/config/global_config.mk") 183 | print("generate config file at: {}".format(config_filename)) 184 | 185 | if project_args.build_type: 186 | build_type = project_args.build_type 187 | elif project_args.release: 188 | build_type = "MinSizeRel" 189 | elif "CMAKE_BUILD_TYPE" in os.environ: 190 | build_type = os.environ["CMAKE_BUILD_TYPE"] 191 | else: 192 | build_type = "Debug" 193 | thread_num = cpu_count() 194 | print("-- CPU count: {}".format(thread_num)) 195 | # config 196 | if project_args.cmd == "config": 197 | print("config complete") 198 | # rebuild / build 199 | elif project_args.cmd == "build" or project_args.cmd == "rebuild": 200 | print("build now") 201 | time_start = time.time() 202 | if not os.path.exists("build"): 203 | os.mkdir("build") 204 | os.chdir("build") 205 | if not os.path.exists("Makefile") or project_args.cmd == "rebuild": 206 | if not os.path.isabs(project_args.config_file): 207 | project_args.config_file = os.path.join(project_path, project_args.config_file) 208 | config_path = os.path.abspath(project_args.config_file) 209 | if not os.path.exists(config_path): 210 | print("config file path error:{}".format(config_path)) 211 | exit(1) 212 | print("-- build type: {}".format(build_type)) 213 | cmd = ["cmake", "-G", configs["CONFIG_CMAKE_GENERATOR"], 214 | "-DCMAKE_BUILD_TYPE={}".format(build_type), 215 | "-DDEFAULT_CONFIG_FILE={}".format(config_path), ".."] 216 | if custom_components_path: 217 | cmd.insert(4, "-DCUSTOM_COMPONENTS_PATH={}".format(custom_components_path)) 218 | res = subprocess.call(cmd) 219 | if res != 0: 220 | exit(1) 221 | if project_args.verbose: 222 | if configs["CONFIG_CMAKE_GENERATOR"] == "Unix Makefiles": 223 | res = subprocess.call(["cmake", "--build", ".", "--target", "all", "--", "VERBOSE=1"]) 224 | elif configs["CONFIG_CMAKE_GENERATOR"] == "Ninja": 225 | res = subprocess.call(["cmake", "--build", ".", "--target", "all", "--", "-v"]) 226 | else: 227 | res = subprocess.call(["cmake", "--build", ".", "--target", "all"]) 228 | else: 229 | if configs["CONFIG_CMAKE_GENERATOR"] in ["Unix Makefiles", "Ninja"]: 230 | res = subprocess.call(["cmake", "--build", ".", "--target", "all", "--", "-j{}".format(thread_num)]) 231 | else: 232 | res = subprocess.call(["cmake", "--build", ".", "--target", "all"]) 233 | if res != 0: 234 | exit(1) 235 | 236 | time_end = time.time() 237 | print("==================================") 238 | print("build end, time last:%.2fs" %(time_end-time_start)) 239 | print("==================================") 240 | # clean 241 | elif project_args.cmd == "clean": 242 | print("clean now") 243 | if os.path.exists("build"): 244 | os.chdir("build") 245 | p =subprocess.Popen(["cmake", "--build", ".", "--target", "clean"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 246 | output, err = p.communicate("") 247 | res = p.returncode 248 | if res == 0: 249 | print(output.decode(encoding="gbk" if os.name == "nt" else "utf-8")) 250 | print("clean complete") 251 | # distclean 252 | elif project_args.cmd == "distclean": 253 | print("clean now") 254 | if os.path.exists("build"): 255 | os.chdir("build") 256 | p =subprocess.Popen(["cmake", "--build", ".", "--target", "clean"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 257 | output, err = p.communicate("") 258 | res = p.returncode 259 | if res == 0: 260 | print(output.decode(encoding="gbk" if os.name == "nt" else "utf-8")) 261 | os.chdir("..") 262 | shutil.rmtree("build") 263 | if os.path.exists("dist"): 264 | shutil.rmtree("dist") 265 | print("clean complete") 266 | # menuconfig 267 | elif project_args.cmd == "menuconfig": 268 | time_start = time.time() 269 | if not os.path.exists("build"): 270 | os.mkdir("build") 271 | os.chdir("build") 272 | binary_path = os.path.abspath(os.getcwd()) 273 | # if not os.path.exists("build/config/global_config.mk"): 274 | # if not os.path.isabs(project_args.config_file): 275 | # project_args.config_file = os.path.join(project_path, project_args.config_file) 276 | # config_path = os.path.abspath(project_args.config_file) 277 | # if not os.path.exists(config_path): 278 | # print("config file path error:{}".format(config_path)) 279 | # exit(1) 280 | # cmd = ["cmake", "-G", configs["CONFIG_CMAKE_GENERATOR"], 281 | # "-DCMAKE_BUILD_TYPE={}".format(build_type), 282 | # "-DDEFAULT_CONFIG_FILE={}".format(config_path), ".."] 283 | # if custom_components_path: 284 | # cmd.insert(4, "-DCUSTOM_COMPONENTS_PATH={}".format(custom_components_path)) 285 | # res = subprocess.call(cmd) 286 | # if res != 0: 287 | # exit(1) 288 | # res = subprocess.call(["cmake", "--build", ".", "--parallel", "1", "--target", "menuconfig"]) 289 | # when use Ninja, menuconfig will not work, so use python script instead, need help here, PR is welcome 290 | tool_path = os.path.join(sdk_path, "tools/kconfig/genconfig.py") 291 | if not os.path.exists(tool_path): 292 | print("[ERROR] kconfig tool not found:", tool_path) 293 | exit(1) 294 | # get default files 295 | config_files = get_config_files(project_args.config_file, sdk_path, project_path) 296 | config_out_path = os.path.join(binary_path, "config") 297 | cmd = [sys.executable, tool_path, "--kconfig", os.path.join(sdk_path, "Kconfig")] 298 | for path in config_files: 299 | cmd.extend(["--defaults", path]) 300 | cmd.extend(["--menuconfig", "True", "--env", "SDK_PATH={}".format(sdk_path), 301 | "--env", "PROJECT_PATH={}".format(project_path), 302 | "--env", "BUILD_TYPE={}".format(build_type)]) 303 | cmd.extend(["--output", "makefile", os.path.join(config_out_path, "global_config.mk")]) 304 | cmd.extend(["--output", "cmake", os.path.join(config_out_path, "global_config.cmake")]) 305 | cmd.extend(["--output", "header", os.path.join(config_out_path, "global_config.h")]) 306 | res = subprocess.call(cmd) 307 | if res != 0: 308 | exit(1) 309 | # clean_conf 310 | elif project_args.cmd == "clean_conf": 311 | print("clean now") 312 | # clean cmake config files 313 | if os.path.exists(config_filename): 314 | os.remove(config_filename) 315 | if os.path.exists("build/config/"): 316 | shutil.rmtree("build/config") 317 | # clean extra tools config file 318 | for k, v in extra_tools.items(): 319 | if project_args.cmd in v["cmds"]: 320 | vars = { 321 | "project_path": project_path, 322 | "project_name": project_name, 323 | "sdk_path": sdk_path, 324 | "build_type": build_type, 325 | "project_parser": project_parser, 326 | "project_args": project_args, 327 | "configs": configs, 328 | } 329 | print("-- call tool <{}>'s clean_conf cmd".format(k)) 330 | v["tool"].main(vars) 331 | print("clean complete") 332 | # extra tools 333 | elif project_args.cmd in cmd_choices: 334 | for k, v in extra_tools.items(): 335 | if project_args.cmd in v["cmds"]: 336 | tool = v["tool"] 337 | vars = { 338 | "project_path": project_path, 339 | "project_name": project_name, 340 | "sdk_path": sdk_path, 341 | "build_type": build_type, 342 | "project_parser": project_parser, 343 | "project_args": project_args, 344 | "configs": configs, 345 | } 346 | print("\n-------- {} start ---------".format(project_args.cmd)) 347 | ret = tool.main(vars) 348 | print("-------- {} end ---------".format(project_args.cmd)) 349 | exit(ret) 350 | else: 351 | print("Error: Unknown command") 352 | exit(1) 353 | 354 | -------------------------------------------------------------------------------- /tools/cmake/sort_components.py: -------------------------------------------------------------------------------- 1 | # 2 | # sort components according to priority.conf file 3 | # 4 | # @file from https://github.com/Neutree/c_cpp_project_framework 5 | # @author neucrack 6 | # @license Apache 2.0 7 | # 8 | # @usage: python sort_components.py priority.conf component_dir1 component_dir2 ... component_dir4 9 | # 10 | 11 | import sys, os 12 | 13 | conf_file = sys.argv[1] 14 | components = sys.argv[2:] 15 | 16 | if not os.path.exists(conf_file): 17 | exit(2) 18 | 19 | try: 20 | conf = "" 21 | f = open(conf_file) 22 | while True: 23 | line = f.readline() 24 | if not line: 25 | break 26 | line = line.strip() 27 | if line.startswith("#") or line == "": 28 | continue 29 | conf += line +" " 30 | f.close() 31 | except Exception as e: 32 | print("[ERROR] "+str(e)) 33 | exit(1) 34 | 35 | components_ordered = conf.split() 36 | dict_order = {} 37 | for i,component in enumerate(components_ordered): 38 | dict_order[component] = i 39 | 40 | final_components = [] 41 | components_not_ordered = [] 42 | for component in components: # all components 43 | name = os.path.basename(component) 44 | if name in dict_order.keys(): # have priority in config file 45 | find_pos = False 46 | if len(final_components) == 0: 47 | find_pos = True 48 | final_components.append(component) 49 | else: 50 | for j,tmp in enumerate(final_components): 51 | tmp_name = os.path.basename(tmp) 52 | if dict_order[name] < dict_order[tmp_name]: 53 | find_pos = True 54 | final_components.insert(j, component) 55 | break 56 | if not find_pos: 57 | final_components.append(component) 58 | else: # no priority in config file 59 | components_not_ordered.append(component) 60 | final_components += components_not_ordered 61 | 62 | for i in final_components: 63 | print(i, end = ";") 64 | 65 | 66 | -------------------------------------------------------------------------------- /tools/cmake/tools.cmake: -------------------------------------------------------------------------------- 1 | 2 | # add prefix for all list members 3 | # uage: prepend(out_var prefix in_var) 4 | function(prepend out prefix) 5 | set(listVar "") 6 | foreach(f ${ARGN}) 7 | list(APPEND listVar "${prefix}${f}") 8 | endforeach(f) 9 | set(${out} "${listVar}" PARENT_SCOPE) 10 | endfunction() 11 | 12 | # convert all members of list to absolute path(relative to CMAKE_CURRENT_SOURCE_DIR) 13 | # usage: abspath(out_var list_var) 14 | function(abspath out) 15 | set(listVar "") 16 | foreach(f ${ARGN}) 17 | list(APPEND listVar "${CMAKE_CURRENT_SOURCE_DIR}/${f}") 18 | endforeach(f) 19 | set(${out} "${listVar}" PARENT_SCOPE) 20 | endfunction() 21 | 22 | 23 | function(append_srcs_dir out_var) 24 | set(listVar ${${out_var}}) 25 | foreach(f ${ARGN}) 26 | aux_source_directory(${f} tmp) 27 | list(APPEND listVar ${tmp}) 28 | endforeach(f) 29 | set(${out_var} "${listVar}" PARENT_SCOPE) 30 | endfunction() 31 | 32 | -------------------------------------------------------------------------------- /tools/cmake/utils.py: -------------------------------------------------------------------------------- 1 | import time 2 | import re 3 | import os 4 | import subprocess 5 | 6 | def check_all_submodule(sdk_path): 7 | if (not os.path.exists(os.path.join(sdk_path, ".gitmodules"))) or (not os.path.exists(os.path.join(sdk_path, ".git"))): 8 | return True, "" 9 | with open(os.path.join(sdk_path, ".gitmodules")) as f: 10 | content = f.read() 11 | m = re.findall(r'path = (.*)', content) 12 | if not m: 13 | return True, "" 14 | for path in m: 15 | full_path = os.path.join(sdk_path, path) 16 | err_msg = "Submodule {} not exists, please run `git submodule update --init --recursive` to init all submodules".format(path) 17 | if (not os.path.exists(full_path)): 18 | print("-- {} not exists".format(full_path)) 19 | return False, err_msg 20 | files = os.listdir(full_path) 21 | if ".git" not in files: 22 | print("-- {}/.git not exists".format(full_path)) 23 | return False, err_msg 24 | visible_files = [] 25 | for name in files: 26 | if not name.startswith("."): 27 | visible_files.append(name) 28 | if len(visible_files) == 0: 29 | print("-- {} no files".format(full_path)) 30 | return False, err_msg 31 | # check if submodule version is the same as should be 32 | cmd = ["git", "submodule", "status"] 33 | p = subprocess.Popen(cmd, cwd=sdk_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 34 | output, err = p.communicate("") 35 | res = p.returncode 36 | if res != 0: 37 | print("-- git submodule status failed") 38 | return False, err 39 | try: 40 | output = output.decode(encoding="utf-8" if os.name == "nt" else "utf-8") 41 | except Exception: 42 | output = output.decode(encoding="gbk" if os.name == "nt" else "utf-8") 43 | lines = output.split("\n") 44 | for line in lines: 45 | if line.startswith("+"): 46 | print("\n============================================") 47 | print("-- [Warning]\n!! Submodule [{}] have changes, it maybe cause problems\nif you don't know what this means, please execute:\n git submodule update --init --recursive`\nto update submodule !".format(line.split(" ")[1])) 48 | print("============================================\n") 49 | time.sleep(1) 50 | elif line.startswith("-"): 51 | return False, "Submodule {} not exists, please run `git submodule update --init --recursive` to init all submodules".format(line.split(" ")[1]) 52 | 53 | return True, "" 54 | -------------------------------------------------------------------------------- /tools/cmds/flash.py: -------------------------------------------------------------------------------- 1 | # 2 | # @file from https://github.com/Neutree/c_cpp_project_framework 3 | # @author neucrack 4 | # @license MIT 5 | # 6 | 7 | import argparse 8 | import os, sys, time, re, shutil 9 | import subprocess 10 | from multiprocessing import cpu_count 11 | import json 12 | 13 | 14 | parser = argparse.ArgumentParser(add_help=False, prog="flash.py") 15 | cmds = ["flash", "clean_conf"] 16 | 17 | ############################### Add option here ############################# 18 | parser.add_argument("-p", "--port", help="flash device port", default="") 19 | parser.add_argument("-b", "--baudrate", type=int, help="flash baudrate", default=115200) 20 | parser.add_argument("-t", "--terminal", help="open terminal after flash ok", default=False, action="store_true") 21 | 22 | dict_arg = {"port":"", 23 | "baudrate": 115200 24 | } 25 | 26 | dict_arg_not_save = ["terminal"] 27 | ############################################################################# 28 | 29 | # use project_args created by SDK_PATH/tools/cmake/project.py, e.g. project_args.terminal 30 | 31 | # args = parser.parse_args() 32 | def main(vars): 33 | ''' 34 | @vars: dict, 35 | "project_path": project_path, 36 | "project_name": project_name, 37 | "sdk_path": sdk_path, 38 | "build_type": build_type, 39 | "project_parser": project_parser, 40 | "project_args": project_args, 41 | "configs": configs, 42 | ''' 43 | firmware = "" 44 | project_path = vars["project_path"] 45 | project_args = vars["project_args"] 46 | project_parser = vars["project_parser"] 47 | try: 48 | flash_conf_path = project_path+"/.flash.conf.json" 49 | if project_args.cmd == "clean_conf": 50 | if os.path.exists(flash_conf_path): 51 | os.remove(flash_conf_path) 52 | return 0 53 | if project_args.cmd != "flash": 54 | print("call flash.py error") 55 | return 1 56 | except Exception: 57 | print("-- call flash.py directly!") 58 | parser.add_argument("firmware", help="firmware file name") 59 | project_parser = parser 60 | project_args = project_parser.parse_args() 61 | project_path = "" 62 | if not os.path.exists(project_args.firmware): 63 | print("firmware not found:{}".format(project_args.firmware)) 64 | return 1 65 | firmware = project_args.firmware 66 | sdk_path = "" 67 | 68 | config_old = {} 69 | # load flash config from file 70 | try: 71 | with open(flash_conf_path, "r") as f: 72 | config_old = json.load(f) 73 | except Exception as e: 74 | pass 75 | # update flash config from args 76 | for key in dict_arg.keys(): 77 | dict_arg[key] = getattr(project_args, key) 78 | # check if config update, if do, use new and update config file 79 | config = {} 80 | for key in config_old: 81 | config[key] = config_old[key] 82 | for key in dict_arg.keys(): 83 | if dict_arg[key] != project_parser.get_default(key): # arg valid, update config 84 | config[key] = dict_arg[key] 85 | else: 86 | if not key in config: 87 | config[key] = dict_arg[key] 88 | if config != config_old: 89 | print("-- flash config changed, update at {}".format(flash_conf_path)) 90 | with open(flash_conf_path, "w+") as f: 91 | json.dump(config, f, indent=4) 92 | 93 | # mask options that not read from file 94 | for key in config: 95 | if key in dict_arg_not_save: 96 | config[key] = dict_arg[key] 97 | 98 | print("== flash start ==") 99 | ############## Add flash command here ################ 100 | print("!!! please write flash ops here ...") 101 | print("-- flash port :{}".format(config["port"])) 102 | print("-- flash baudrate:{}".format(config["baudrate"])) 103 | print("project path:{}".format(project_path)) 104 | 105 | if config["port"] == "": 106 | print("[ERROR] Invalid port:{}, set by -p or --port, e.g. -p /dev/ttyUSB0".format(config["port"])) 107 | return 1 108 | 109 | ###################################################### 110 | print("== flash end ==") 111 | 112 | if __name__ == '__main__': 113 | main() 114 | -------------------------------------------------------------------------------- /tools/cmds/run.py: -------------------------------------------------------------------------------- 1 | # 2 | # @file from https://github.com/Neutree/c_cpp_project_framework 3 | # @author neucrack 4 | # @license MIT 5 | # 6 | 7 | import argparse 8 | import os 9 | import subprocess 10 | 11 | parser = argparse.ArgumentParser(add_help=False, prog="run.py") 12 | cmds = ["run"] 13 | 14 | ############################### Add option here ############################# 15 | # parser.add_argument("-t", "--terminal", help="open terminal after flash ok", default=False, action="store_true") 16 | 17 | ############################################################################# 18 | 19 | # use project_args created by SDK_PATH/tools/cmake/project.py, e.g. project_args.terminal 20 | 21 | def main(vars): 22 | ''' 23 | @vars: dict, 24 | "project_path": project_path, 25 | "project_name": project_name, 26 | "sdk_path": sdk_path, 27 | "build_type": build_type, 28 | "project_parser": project_parser, 29 | "project_args": project_args, 30 | "configs": configs, 31 | ''' 32 | exec_path = os.path.join(vars["project_path"], "build", vars["project_name"]) 33 | print("exec_path:{}".format(exec_path)) 34 | print("---------------------") 35 | # execute exec_path file with subprocess 36 | cmd = [exec_path] 37 | exit_code = subprocess.Popen(cmd).wait() 38 | print("---------------------") 39 | if exit_code < 0: 40 | print("[ERROR] Program exit with code:{}".format(exit_code)) 41 | 42 | # args = parser.parse_args() 43 | if __name__ == '__main__': 44 | main() 45 | -------------------------------------------------------------------------------- /tools/kconfig/genconfig.py: -------------------------------------------------------------------------------- 1 | # 2 | # @file from https://github.com/Neutree/c_cpp_project_framework 3 | # @author neucrack 4 | # 5 | 6 | import argparse 7 | import os, sys 8 | 9 | kconfig_lib_path = sys.path[0]+"/Kconfiglib" 10 | sys.path.append(kconfig_lib_path) 11 | 12 | import kconfiglib 13 | from menuconfig import menuconfig 14 | 15 | 16 | def _cmake_contents(kconfig, header): 17 | chunks = [header] 18 | add = chunks.append 19 | config_vars = [] 20 | for sym in kconfig.unique_defined_syms: 21 | # _write_to_conf is determined when the value is calculated. This 22 | # is a hidden function call due to property magic. 23 | val = sym.str_value 24 | if not sym._write_to_conf: 25 | continue 26 | if sym.orig_type in (kconfiglib.BOOL, kconfiglib.TRISTATE) and val == "n": 27 | val = "" 28 | add("set({}{} \"{}\")\n".format( 29 | kconfig.config_prefix, sym.name, val)) 30 | config_vars.append(str(kconfig.config_prefix+sym.name)) 31 | add("set(CONFIGS_LIST {})\n".format(";".join(config_vars))) 32 | return "".join(chunks) 33 | 34 | 35 | def write_config(kconfig, filename, gui): 36 | print("-- Write makefile config at: " + filename) 37 | if not gui: 38 | kconfig.write_config(filename) 39 | 40 | def write_cmake(kconfig, filename, gui): 41 | print("-- Write cmake config at: " + filename) 42 | cmake_conf_header = "# Generated by c_cpp_project_framework(https://github.com/Neutree/c_cpp_project_framework)\n" 43 | cmake_conf_header += "### DO NOT edit this file!! ###\n\n" 44 | cmake_conf_content = _cmake_contents(kconfig, cmake_conf_header) 45 | # don't change file info if config no change 46 | if os.path.exists(filename): 47 | with open(filename) as f: 48 | if f.read() == cmake_conf_content: 49 | return 50 | f = open(filename, "w") 51 | f.write(cmake_conf_content) 52 | f.close() 53 | 54 | 55 | def write_header(kconfig, filename, gui): 56 | print("-- write c header file at: " + filename) 57 | # kconfig.write_autoconf(filename) 58 | header="/* Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib) */\n" 59 | old = "" 60 | if os.path.exists(filename): 61 | with open(filename, "r") as f: 62 | old = f.read() 63 | new = kconfig._autoconf_contents(header) 64 | os.environ["BUILD_TYPE"] 65 | build_type = os.environ.get("BUILD_TYPE", "debug").lower() 66 | if build_type in ["release", "minsizerel", "relwithdebinfo"]: 67 | print("-- build type:{}, write RELEASE definition to c header".format(build_type)) 68 | new += "#define RELEASE 1\n#define DEBUG 0\n" 69 | else: 70 | print("-- build type:{}, write DEBUG definition to c header".format(build_type)) 71 | new += "#define DEBUG 1\n#define RELEASE 0\n" 72 | if old == new: 73 | print("-- c header file not changed, skip write") 74 | return 75 | with open(filename, "w") as f: 76 | f.write(new) 77 | 78 | OUTPUT_FORMATS = {"makefile": write_config, 79 | "header": write_header, 80 | "cmake": write_cmake 81 | } 82 | 83 | parser = argparse.ArgumentParser(description='menuconfig tool', prog=os.path.basename(sys.argv[0])) 84 | 85 | parser.add_argument('--kconfig', 86 | help='KConfig file', 87 | default='Kconfig', 88 | metavar='FILENAME', 89 | required=None) 90 | 91 | parser.add_argument('--defaults', 92 | action='append', 93 | default=[], 94 | help='Optional project defaults file. ' 95 | 'Multiple files can be specified using multiple --defaults arguments.', 96 | metavar="FILENAME" 97 | ) 98 | 99 | parser.add_argument('--output', nargs=2, action='append', 100 | help='Write output file (format and output filename)', 101 | metavar=('FORMAT', 'FILENAME'), 102 | default=[]) 103 | 104 | parser.add_argument('--env', 105 | action='append', 106 | default=[], 107 | help='Environment to set when evaluating the config file', 108 | metavar='VAR=VALUE' 109 | ) 110 | 111 | parser.add_argument("--menuconfig", 112 | help="Open menuconfig GUI interface", 113 | choices=["False", "True"], 114 | default="False", 115 | ) 116 | 117 | args = parser.parse_args() 118 | 119 | for env in args.env: 120 | env = env.split("=") 121 | var = env[0] 122 | value = env[1] 123 | os.environ[var] = value 124 | 125 | out_format = {"makefile": ".config"} 126 | for fmt, filename in args.output: 127 | if fmt not in OUTPUT_FORMATS.keys(): 128 | print("Format %s not supported! Known formats:%s" %(fmt, OUTPUT_FORMATS.keys())) 129 | sys.exit(1) 130 | out_format[fmt] = filename 131 | 132 | if out_format["makefile"] != ".config": 133 | os.environ["KCONFIG_CONFIG"] = out_format["makefile"] 134 | 135 | kconfig = kconfiglib.Kconfig(args.kconfig) 136 | 137 | # load config, so if config file exist, the default file may 138 | # not take effect, if want to use default, 139 | # remove the config file in build directory 140 | if not os.path.exists(out_format["makefile"]): 141 | for path in args.defaults: 142 | if not os.path.exists(path): 143 | raise ValueError("Path %s not found!" %(path)) 144 | print("-- Load default:", path) 145 | kconfig.load_config(path, replace=False) 146 | else: 147 | kconfig.load_config() 148 | 149 | if args.menuconfig == "True": 150 | menuconfig(kconfig) 151 | 152 | # write back 153 | for fmt, filename in out_format.items(): 154 | dir = os.path.split(filename)[0] 155 | if not os.path.exists(dir): 156 | os.makedirs(dir) 157 | 158 | for fmt, filename in out_format.items(): 159 | func = OUTPUT_FORMATS[fmt] 160 | func(kconfig, filename, args.menuconfig == "True") 161 | 162 | 163 | -------------------------------------------------------------------------------- /tools/kconfig/update_build_info.py: -------------------------------------------------------------------------------- 1 | # 2 | # @file from https://github.com/Neutree/c_cpp_project_framework 3 | # @author neucrack 4 | # 5 | 6 | import argparse 7 | import os, sys, time, re 8 | import subprocess 9 | 10 | 11 | time_str_header = ''' 12 | #define BUILD_TIME_YEAR {} 13 | #define BUILD_TIME_MONTH {} 14 | #define BUILD_TIME_DAY {} 15 | #define BUILD_TIME_HOUR {} 16 | #define BUILD_TIME_MINUTE {} 17 | #define BUILD_TIME_SECOND {} 18 | #define BUILD_TIME_WEEK_OF_DAY {} 19 | #define BUILD_TIME_YEAR_OF_DAY {} 20 | ''' 21 | 22 | time_str_cmake = ''' 23 | set(BUILD_TIME_YEAR "{}") 24 | set(BUILD_TIME_MONTH "{}") 25 | set(BUILD_TIME_DAY "{}") 26 | set(BUILD_TIME_HOUR "{}") 27 | set(BUILD_TIME_MINUTE "{}") 28 | set(BUILD_TIME_SECOND "{}") 29 | set(BUILD_TIME_WEEK_OF_DAY "{}") 30 | set(BUILD_TIME_YEAR_OF_DAY "{}") 31 | ''' 32 | 33 | time_str_makefile = ''' 34 | BUILD_TIME_YEAR ={} 35 | BUILD_TIME_MONTH ={} 36 | BUILD_TIME_DAY ={} 37 | BUILD_TIME_HOUR ={} 38 | BUILD_TIME_MINUTE ={} 39 | BUILD_TIME_SECOND ={} 40 | BUILD_TIME_WEEK_OF_DAY ={} 41 | BUILD_TIME_YEAR_OF_DAY ={} 42 | ''' 43 | 44 | git_str_header = ''' 45 | #define BUILD_VERSION_MAJOR {} 46 | #define BUILD_VERSION_MINOR {} 47 | #define BUILD_VERSION_MICRO {} 48 | #define BUILD_VERSION_DEV {} 49 | #define BUILD_GIT_COMMIT_ID "{}" 50 | #define BUILD_GIT_IS_DIRTY {} 51 | ''' 52 | 53 | git_str_cmake = ''' 54 | set(BUILD_VERSION_MAJOR "{}") 55 | set(BUILD_VERSION_MINOR "{}") 56 | set(BUILD_VERSION_MICRO "{}") 57 | set(BUILD_VERSION_DEV "{}") 58 | set(BUILD_GIT_COMMIT_ID "{}") 59 | set(BUILD_GIT_IS_DIRTY "{}") 60 | ''' 61 | 62 | git_str_makefile = ''' 63 | BUILD_VERSION_MAJOR ={} 64 | BUILD_VERSION_MINOR ={} 65 | BUILD_VERSION_MICRO ={} 66 | BUILD_VERSION_DEV ={} 67 | BUILD_GIT_COMMIT_ID ={} 68 | BUILD_GIT_IS_DIRTY ={} 69 | ''' 70 | 71 | str_define_start_makefile = "\n# compile append define start\n" 72 | str_define_end_makefile = "\n# compile append define end\n" 73 | str_define_start_cmake = "\n# compile append define start\n" 74 | str_define_end_cmake = "\n# compile append define end\n" 75 | str_define_start_header = "\n//compile append define start\n" 76 | str_define_end_header = "\n//compile append define end\n" 77 | 78 | INFO_FORMAT_STR = {"header": [str_define_start_header, str_define_end_header, time_str_header, git_str_header], 79 | "cmake": [str_define_start_cmake, str_define_end_cmake, time_str_cmake, git_str_cmake], 80 | "makefile": [str_define_start_makefile, str_define_end_makefile, time_str_makefile, git_str_makefile], 81 | } 82 | 83 | def remove_old_config_info(start_flag_str, end_flag_str, content): 84 | match = re.findall(r"{}(.*){}".format(start_flag_str, end_flag_str), content, re.MULTILINE|re.DOTALL) 85 | if len(match) == 0: 86 | content += start_flag_str+end_flag_str 87 | else: 88 | content = content.replace(match[0], "") 89 | return content 90 | 91 | def append_time_info(time_info_filename, version_info_filename, file_type): 92 | str_time_define_start = INFO_FORMAT_STR[file_type][0] 93 | str_time_define_end = INFO_FORMAT_STR[file_type][1] 94 | append_format_time_str= INFO_FORMAT_STR[file_type][2] 95 | append_format_git_str = INFO_FORMAT_STR[file_type][3] 96 | content = "" 97 | content2 = "" 98 | content2_old = content2 99 | try: 100 | f = open(time_info_filename) 101 | content = f.read() 102 | f.close() 103 | except Exception: 104 | pass 105 | if version_info_filename: 106 | try: 107 | f = open(version_info_filename) 108 | content2 = f.read() 109 | content2_old = content2 110 | f.close() 111 | except Exception: 112 | pass 113 | time_now = time.localtime(time.time()) 114 | # remove old config info 115 | content = remove_old_config_info(str_time_define_start, str_time_define_end, content) 116 | content2 = remove_old_config_info(str_time_define_start, str_time_define_end, content2) 117 | 118 | # time info 119 | time_define = append_format_time_str.format(time_now.tm_year, 120 | time_now.tm_mon, 121 | time_now.tm_mday, 122 | time_now.tm_hour, 123 | time_now.tm_min, 124 | time_now.tm_sec, 125 | time_now.tm_wday, 126 | time_now.tm_yday) 127 | # git info 128 | # add tag by command; 129 | # git tag -a v0.1.1 -m "release v0.1.1 describe....." 130 | # git push origin --tags 131 | git_tag_name = "" 132 | version_major = 0 133 | version_minor = 0 134 | version_micro = 0 135 | version_dev = 0 136 | git_hash = "" 137 | git_dirty = "" 138 | try: 139 | git_tag = subprocess.check_output(["git", "describe", "--long", "--tag", "--dirty", "--always"], stderr=subprocess.STDOUT, universal_newlines=True).strip() 140 | except subprocess.CalledProcessError as er: 141 | if er.returncode == 128: 142 | # git exit code of 128 means no repository found 143 | print("== WARNING: NOT a git repository !!!") 144 | git_tag = "" 145 | except OSError: 146 | git_tag = "" 147 | # git_tag = "v0.3.2-39-gbeae86483-dirty" 148 | git_tag = git_tag.split("-") 149 | if len(git_tag) == 0: 150 | print("== WARNING: git get info fail") 151 | if len(git_tag) == 1: # bdc1dcf 152 | git_hash = git_tag[0] 153 | elif len(git_tag) == 2: # bdc1dcf-dirty or v0.1.1-bdc1dcf 154 | if git_tag[1] == "dirty": 155 | git_hash = git_tag[0] 156 | git_dirty = git_tag[1] 157 | else: 158 | git_tag_name = git_tag[0] 159 | git_hash = git_tag[1] 160 | elif len(git_tag) == 3: # v0.1.1-10-bdc1dcf or v0.1.1-bdc1dcf-dirty 161 | if git_tag[2] == "dirty": 162 | git_tag_name = git_tag[0] 163 | git_hash = git_tag[1] 164 | git_dirty = git_tag[2] 165 | else: 166 | git_tag_name = git_tag[0]+"."+git_tag[1] 167 | git_hash = git_tag[2] 168 | else: # v0.1.1-10-bdc1dcf-dirty 169 | git_tag_name = git_tag[0]+"."+git_tag[1] 170 | git_hash = git_tag[2] 171 | git_dirty = git_tag[3] 172 | 173 | if git_tag_name.lower().startswith("v"): 174 | version = git_tag_name[1:].split(".") 175 | # convert to int from str 176 | for i,v in enumerate(version): 177 | try: 178 | version[i] = int(v) 179 | except Exception: 180 | version[i] = 0 181 | if len(version) >= 1: 182 | version_major = version[0] 183 | if len(version) >= 2: 184 | version_minor = version[1] 185 | if len(version) >= 3: 186 | version_micro = version[2] 187 | if len(version) >= 4: 188 | version_dev = version[3] 189 | if file_type == "header": 190 | dirty_value = 1 if git_dirty=="dirty" else 0 191 | elif file_type == "cmake": 192 | dirty_value = "y" if git_dirty=="dirty" else "" 193 | else: 194 | if git_dirty=="dirty": 195 | dirty_value = "y" 196 | else: 197 | append_format_git_str = append_format_git_str.replace("BUILD_GIT_IS_DIRTY", "# BUILD_GIT_IS_DIRTY") 198 | dirty_value = " is not set" 199 | git_define = append_format_git_str.format(version_major, 200 | version_minor, 201 | version_micro, 202 | version_dev, 203 | git_hash, 204 | dirty_value) 205 | # append time and git info to content 206 | content = content.split(str_time_define_end) 207 | if not version_info_filename: 208 | content = (time_define+git_define+str_time_define_end).join(content) 209 | else: 210 | content2 = content2.split(str_time_define_end) 211 | content = (time_define+str_time_define_end).join(content) 212 | content2 = (git_define+str_time_define_end).join(content2) 213 | # update config file 214 | os.makedirs(os.path.dirname(time_info_filename), exist_ok=True) 215 | with open(time_info_filename, "w") as f: 216 | f.write(content) 217 | if version_info_filename and content2 != content2_old: 218 | os.makedirs(os.path.dirname(version_info_filename), exist_ok=True) 219 | with open(version_info_filename, "w") as f: 220 | f.write(content2) 221 | 222 | def write_config(filename): 223 | print("-- Update build time and version info to makefile config at: " + str(filename)) 224 | time_info_filename = None 225 | version_info_filename = None 226 | if filename[0] != None and filename[0].lower() != "none" and os.path.exists(filename[0]): 227 | time_info_filename = filename[0] 228 | if filename[1] != None and filename[1].lower() != "none" and os.path.exists(filename[1]): 229 | version_info_filename = filename[1] 230 | if time_info_filename == None: 231 | raise Exception("param error") 232 | append_time_info(time_info_filename, version_info_filename, "makefile") 233 | 234 | def write_cmake(filename): 235 | print("-- Update build time and version info to cmake config at: " + str(filename)) 236 | time_info_filename = None 237 | version_info_filename = None 238 | if filename[0] != None and filename[0].lower() != "none" and os.path.exists(filename[0]): 239 | time_info_filename = filename[0] 240 | if filename[1] != None and filename[1].lower() != "none" and os.path.exists(filename[1]): 241 | version_info_filename = filename[1] 242 | if time_info_filename == None: 243 | raise Exception("param error") 244 | append_time_info(time_info_filename, version_info_filename, "cmake") 245 | 246 | def write_header(filename): 247 | print("-- Update build time and version info to header config at: " + str(filename)) 248 | time_info_filename = None 249 | version_info_filename = None 250 | if filename[0] != None and filename[0].lower() != "none": 251 | time_info_filename = filename[0] 252 | if filename[1] != None and filename[1].lower() != "none": 253 | version_info_filename = filename[1] 254 | if time_info_filename == None: 255 | raise Exception("param error") 256 | append_time_info(time_info_filename, version_info_filename, "header") 257 | 258 | parser = argparse.ArgumentParser(description='generate time info for', prog=os.path.basename(sys.argv[0])) 259 | 260 | OUTPUT_FORMATS = {"makefile": write_config, 261 | "header": write_header, 262 | "cmake": write_cmake 263 | } 264 | 265 | 266 | parser.add_argument('--configfile', nargs=3, action='append', 267 | help='Write config file (format and output filename), version_filename can be None so all version info will append to time_filename', 268 | metavar=('FORMAT', 'TIME_FILENAME', "VERSION_FILENAME"), 269 | default=[]) 270 | 271 | args = parser.parse_args() 272 | 273 | out_format = {} 274 | for fmt, filename, version_filename in args.configfile: 275 | if fmt not in OUTPUT_FORMATS.keys(): 276 | print("Format %s not supported! Known formats:%s" %(fmt, OUTPUT_FORMATS.keys())) 277 | sys.exit(1) 278 | out_format[fmt] = (filename, version_filename) 279 | 280 | for fmt, filename in out_format.items(): 281 | # if not os.path.exists(filename): 282 | # print("File not found:%s" %(filename)) 283 | # not check always create 284 | func = OUTPUT_FORMATS[fmt] 285 | func(filename) 286 | 287 | 288 | --------------------------------------------------------------------------------