├── .clang-format ├── .editorconfig ├── .github ├── CONTRIBUTING.zh-CN.md ├── dependabot.yml └── workflows │ ├── android-x86-cpu.yml │ ├── code-format.yml │ ├── rebase.yml │ ├── test-coverage.yml │ └── unit-test.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake └── summary.cmake ├── shufaCV ├── Mat.cpp └── Mat.h ├── tests ├── test_mat.cpp └── test_version.cpp ├── tools └── run_clang_format.py └── version.h.in /.clang-format: -------------------------------------------------------------------------------- 1 | # find shufaCV/ tests/ -type f -name '*.c' -o -name '*.cpp' -o -name '*.h' | xargs -i clang-format -i {} 2 | 3 | # need clang-format >= 10.0 4 | 5 | AccessModifierOffset: -4 6 | AlignAfterOpenBracket: Align 7 | AlignConsecutiveAssignments: false 8 | # AlignConsecutiveBitFields: true 9 | AlignConsecutiveDeclarations: false 10 | AlignConsecutiveMacros: true 11 | AlignEscapedNewlines: Left 12 | # AlignOperands: AlignAfterOperator 13 | AlignTrailingComments: true 14 | AllowAllArgumentsOnNextLine: true 15 | AllowAllConstructorInitializersOnNextLine: true 16 | AllowAllParametersOfDeclarationOnNextLine: true 17 | AllowShortBlocksOnASingleLine: Always 18 | AllowShortCaseLabelsOnASingleLine: true 19 | # AllowShortEnumsOnASingleLine: true 20 | AllowShortFunctionsOnASingleLine: None 21 | AllowShortIfStatementsOnASingleLine: WithoutElse 22 | AllowShortLambdasOnASingleLine: All 23 | AllowShortLoopsOnASingleLine: true 24 | AlwaysBreakAfterReturnType: None 25 | AlwaysBreakBeforeMultilineStrings: false 26 | AlwaysBreakTemplateDeclarations: Yes 27 | BinPackArguments: true 28 | BinPackParameters: true 29 | BraceWrapping: 30 | AfterCaseLabel: true 31 | AfterClass: true 32 | AfterControlStatement: Always 33 | AfterEnum: true 34 | AfterFunction: true 35 | AfterNamespace: false 36 | AfterObjCDeclaration: false 37 | AfterStruct: true 38 | AfterUnion: true 39 | AfterExternBlock: false 40 | BeforeCatch: true 41 | BeforeElse: true 42 | # BeforeLambdaBody: false 43 | # BeforeWhile: false 44 | IndentBraces: false 45 | SplitEmptyFunction: true 46 | SplitEmptyRecord: true 47 | SplitEmptyNamespace: false 48 | BreakAfterJavaFieldAnnotations: true 49 | BreakBeforeBinaryOperators: All 50 | BreakBeforeBraces: Custom 51 | BreakBeforeTernaryOperators: true 52 | BreakConstructorInitializers: BeforeColon 53 | BreakInheritanceList: BeforeColon 54 | BreakStringLiterals: false 55 | ColumnLimit: 0 56 | # CommentPragmas: 57 | CompactNamespaces: false 58 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 59 | ConstructorInitializerIndentWidth: 4 60 | ContinuationIndentWidth: 4 61 | Cpp11BracedListStyle: true 62 | DeriveLineEnding: false 63 | DerivePointerAlignment: false 64 | # DisableFormat: 65 | # ExperimentalAutoDetectBinPacking: 66 | FixNamespaceComments: true 67 | # ForEachMacros: 68 | IncludeBlocks: Regroup 69 | # IncludeCategories: 70 | # IncludeIsMainRegex: 71 | # IncludeIsMainSourceRegex: 72 | # IndentCaseBlocks: false 73 | IndentCaseLabels: false 74 | # IndentExternBlock: NoIndent 75 | IndentGotoLabels: false 76 | IndentPPDirectives: None 77 | IndentWidth: 4 78 | # IndentWrappedFunctionNames: 4 79 | # InsertTrailingCommas: None 80 | # JavaImportGroups: 81 | # JavaScriptQuotes 82 | # JavaScriptWrapImports: 83 | KeepEmptyLinesAtTheStartOfBlocks: false 84 | Language: Cpp 85 | # MacroBlockBegin: 86 | # MacroBlockEnd: 87 | MaxEmptyLinesToKeep: 1 88 | NamespaceIndentation: None 89 | # NamespaceMacros: 90 | # ObjCBinPackProtocolList: 91 | # ObjCBlockIndentWidth: 92 | # ObjCBreakBeforeNestedBlockParam: 93 | # ObjCSpaceAfterProperty: 94 | # ObjCSpaceBeforeProtocolList: 95 | # PenaltyBreakAssignment: 96 | # PenaltyBreakBeforeFirstCallParameter: 97 | # PenaltyBreakComment: 98 | # PenaltyBreakFirstLessLess: 99 | # PenaltyBreakString: 100 | # PenaltyBreakTemplateDeclaration: 101 | # PenaltyExcessCharacter: 102 | # PenaltyReturnTypeOnItsOwnLine: 103 | PointerAlignment: Left 104 | # RawStringFormats: 105 | ReflowComments: false 106 | SortIncludes: false 107 | SortUsingDeclarations: true 108 | SpaceAfterCStyleCast: false 109 | SpaceAfterLogicalNot: false 110 | SpaceAfterTemplateKeyword: false 111 | SpaceBeforeAssignmentOperators: true 112 | SpaceBeforeCpp11BracedList: false 113 | SpaceBeforeCtorInitializerColon: true 114 | SpaceBeforeInheritanceColon: true 115 | SpaceBeforeParens: ControlStatements 116 | SpaceBeforeRangeBasedForLoopColon: true 117 | SpaceBeforeSquareBrackets: false 118 | SpaceInEmptyBlock: false 119 | SpaceInEmptyParentheses: false 120 | SpacesBeforeTrailingComments: 1 121 | SpacesInAngles: false 122 | SpacesInCStyleCastParentheses: false 123 | SpacesInConditionalStatement: false 124 | SpacesInContainerLiterals: false 125 | SpacesInParentheses: false 126 | SpacesInSquareBrackets: false 127 | Standard: c++03 128 | #StatementMacros: 129 | TabWidth: 4 130 | # TypenameMacros: 131 | UseCRLF: false 132 | UseTab: Never 133 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org/ 2 | 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 4 12 | 13 | [{CMakeLists.*,*.cmake}] 14 | indent_style = space 15 | indent_size = 2 16 | 17 | [Makefile] 18 | indent_style = tab 19 | 20 | [*.{bat,cmd,cmd.*}] 21 | end_of_line = crlf 22 | indent_style = space 23 | indent_size = 2 24 | 25 | [*.{ps1,ps1.*}] 26 | end_of_line = crlf 27 | indent_style = space 28 | indent_size = 4 29 | 30 | [*.{md,markdown}] 31 | indent_size = 4 32 | 33 | [*.yml] 34 | indent_style = space 35 | indent_size = 2 36 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.zh-CN.md: -------------------------------------------------------------------------------- 1 | ## 如何提交代码 2 | 3 | ### 一、fork 分支 4 | 5 | 在浏览器中打开 [shufaCV](https://github.com/scarsty/shufaCV/), `fork` 到自己的 repositories,例如 6 | 7 | ```shell 8 | https://github.com/zchrissirhcz/shufaCV.git 9 | ``` 10 | 11 | clone 项目到本地,添加官方 remote 并 fetch: 12 | 13 | ```shell 14 | git clone https://github.com/zchrissirhcz/shufaCV.git && cd shufaCV 15 | git remote add upstream https://github.com/scarsty/shufaCV.git 16 | git fetch upstream 17 | ``` 18 | 19 | 对于 `git clone` 下来的项目,它现在有两个 remote,分别是 origin 和 upstream: 20 | 21 | ```shell 22 | git remote -v 23 | origin https://github.com/zchrissirhcz/shufaCV.git (fetch) 24 | origin https://github.com/zchrissirhcz/shufaCV.git (push) 25 | upstream https://github.com/scarsty/shufaCV.git (fetch) 26 | upstream https://github.com/scarsty/shufaCV.git (push) 27 | ``` 28 | 29 | origin 指向你 fork 的仓库地址;upstream 即官方 repo。可以基于不同的 remote 创建和提交分支。 30 | 31 | 例如切换到官方 main 分支,并基于此创建自己的分支(命名尽量言简意赅。一个分支只做一件事,方便 review 和 revert) 32 | 33 | ```shell 34 | git rebase upstream/main 35 | git checkout -b fix-readme 36 | ``` 37 | 38 | 或创建分支时指定基于官方 main 分支: 39 | 40 | ```shell 41 | git checkout -b fix-readme upstream/main 42 | ``` 43 | 44 | > `git fetch` 是从远程获取最新代码到本地。如果是第二次 pr shufaCV,直接从 `git fetch upstream` 开始即可,不需要 `git remote add upstream`,也不需要修改 `github.com/zchrissirhcz/shufaCV`。 45 | 46 | ### 二、代码习惯 47 | 48 | 为了增加沟通效率,reviewer 一般要求 contributor 遵从以下规则 49 | 50 | * `if-else` 和花括号 `{` 中间需要换行 51 | * 不能随意增删空行 52 | * tab 替换为 4 个空格 53 | * 为了保证平台兼容性,尽量增加CI测试 54 | * 若是新增功能或平台,`test` 目录需有对应测试用例 55 | * 文档放到 `doc` 对应目录下,中文用 `.zh-CN.md` 做后缀;英文直接用 `.md` 后缀 56 | * 同时我们也提供了如下 python 脚本在本地自动化修复格式 57 | 58 | ```shell 59 | python tools/run_clang_format.py --files shufaCV tests --recursive \ 60 | --clang_format_executable clang-format --style file --fix 61 | ``` 62 | 63 | 开发完成后提交到自己的 repository 64 | 65 | ```shell 66 | git commit -a 67 | git push origin fix-readme 68 | ``` 69 | 70 | 推荐使用 [`commitizen`](https://pypi.org/project/commitizen/) 或 [`gitlint`](https://jorisroovers.com/gitlint/) 等工具格式化 commit message,方便事后检索海量提交记录 71 | 72 | ### 三、代码提交 73 | 74 | 浏览器中打开 [scarsty pulls](https://github.com/scarsty/shufaCV/pulls) ,此时应有此分支 pr 提示,点击 `Compare & pull request` 75 | 76 | * 标题**必须**是英文。未完成的分支应以 `WIP:` 开头,例如 `WIP: add conv int8` 77 | * 正文宜包含以下内容,中英不限 78 | * 内容概述和实现方式 79 | * 功能或性能测试 80 | * 测试结果 81 | 82 | CI 已集成了自动格式化,需要 merge 自动 restyled 的分支,例如 83 | 84 | ```shell 85 | git fetch upstream 86 | git checkout fix-readme 87 | git merge upstream/restyled/pull-22 88 | git push origin fix-readme 89 | ``` 90 | 91 | 回到浏览器签署 CLA,所有 CI 测试通过后通知 reviewer merge 此分支。 92 | 93 | ### 四、彩蛋 94 | 95 | 留下个人 qq 号会触发隐藏事件。 96 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: github-actions 9 | directory: "/" 10 | schedule: 11 | interval: weekly 12 | reviewers: 13 | - zhiqwang 14 | labels: 15 | - dependencies 16 | -------------------------------------------------------------------------------- /.github/workflows/android-x86-cpu.yml: -------------------------------------------------------------------------------- 1 | name: android-x86-cpu 2 | on: 3 | push: 4 | branches: [master] 5 | paths: 6 | - '.github/workflows/android-x86-cpu.yml' 7 | - 'CMakeLists.txt' 8 | - 'cmake/**' 9 | - 'shufaCV/*' 10 | pull_request: 11 | branches: [master] 12 | paths: 13 | - '.github/workflows/android-x86-cpu.yml' 14 | - 'CMakeLists.txt' 15 | - 'cmake/**' 16 | - 'shufaCV/*' 17 | jobs: 18 | android-x86: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: cancel-previous-runs 22 | uses: styfle/cancel-workflow-action@0.9.1 23 | with: 24 | access_token: ${{ secrets.GITHUB_TOKEN }} 25 | - uses: actions/checkout@v2 26 | - name: build 27 | run: | 28 | mkdir build && cd build 29 | cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_HOME/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_ABI="x86" -DANDROID_PLATFORM=android-14 .. 30 | cmake --build . -j 2 31 | -------------------------------------------------------------------------------- /.github/workflows/code-format.yml: -------------------------------------------------------------------------------- 1 | name: Code format 2 | 3 | on: [push, pull_request, pull_request_target] 4 | 5 | jobs: 6 | clang-format: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Clone repository 11 | uses: actions/checkout@v2 12 | - name: Run clang format 13 | run: | 14 | curl https://oss-clang-format.s3.us-east-2.amazonaws.com/linux64/clang-format-linux64 -o clang-format 15 | chmod +x clang-format 16 | sudo mv clang-format /opt/clang-format 17 | python tools/run_clang_format.py --files shufaCV tests --recursive --clang_format_executable /opt/clang-format --style file --fix 18 | - name: Apply code-format changes 19 | uses: stefanzweifel/git-auto-commit-action@v4 20 | with: 21 | commit_message: Apply code-format changes 22 | -------------------------------------------------------------------------------- /.github/workflows/rebase.yml: -------------------------------------------------------------------------------- 1 | name: Automatic Rebase 2 | # https://github.com/marketplace/actions/automatic-rebase 3 | 4 | on: 5 | issue_comment: 6 | types: [created] 7 | 8 | jobs: 9 | rebase: 10 | name: Rebase 11 | if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase') 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout the latest code 15 | uses: actions/checkout@v2 16 | with: 17 | fetch-depth: 0 18 | - name: Automatic Rebase 19 | uses: cirrus-actions/rebase@1.5 20 | env: 21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yml: -------------------------------------------------------------------------------- 1 | # GH actions 2 | 3 | name: test-coverage 4 | 5 | on: 6 | push: 7 | branches: [ main ] 8 | pull_request: 9 | branches: [ main ] 10 | 11 | env: 12 | BUILD_TYPE: Release 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Clone repository 20 | uses: actions/checkout@v2 21 | 22 | - name: Setup Ninja 23 | uses: seanmiddleditch/gha-setup-ninja@v3 24 | 25 | - name: Install googletest 26 | shell: bash 27 | run: | 28 | git clone https://github.com/google/googletest --depth 1 29 | cd googletest 30 | mkdir build && cd build 31 | cmake .. -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/googletest-install -G Ninja 32 | cmake --build . --config ${{env.BUILD_TYPE}} -j 2 33 | cmake --install . --config ${{env.BUILD_TYPE}} 34 | 35 | - name: Install lcov 36 | run: sudo apt-get install lcov 37 | 38 | - name: Configure with CMake 39 | shell: bash 40 | run: | 41 | mkdir build 42 | cd build 43 | cmake -G Ninja .. -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DSHUFACV_TEST=ON -DSHUFACV_COVERAGE=ON -DGTest_DIR=${{github.workspace}}/googletest-install/lib/cmake/GTest 44 | - name: Building 45 | run: cmake --build build --config ${{env.BUILD_TYPE}} 46 | - name: Testing 47 | working-directory: ${{github.workspace}}/build 48 | run: ctest -C ${{env.BUILD_TYPE}} --output-on-failure 49 | 50 | - name: lcov-collect 51 | run: | 52 | cd build 53 | lcov -d . -c -o lcov.info 54 | lcov -r lcov.info '/usr/*' -o lcov.info 55 | lcov -r lcov.info '*/build/*' -o lcov.info 56 | lcov --list lcov.info 57 | - name: codecov 58 | uses: codecov/codecov-action@v2.1.0 59 | with: 60 | token: ${{ secrets.CODECOV_TOKEN }} 61 | file: build/lcov.info 62 | -------------------------------------------------------------------------------- /.github/workflows/unit-test.yml: -------------------------------------------------------------------------------- 1 | # GH actions 2 | 3 | name: unittest 4 | 5 | on: 6 | push: 7 | branches: [ main ] 8 | pull_request: 9 | branches: [ main ] 10 | 11 | env: 12 | BUILD_TYPE: Release 13 | 14 | jobs: 15 | build: 16 | runs-on: ${{ matrix.image }} 17 | strategy: 18 | matrix: 19 | image: [ 'windows-latest', 'ubuntu-latest', 'macos-latest' ] 20 | include: 21 | - image: windows-latest 22 | cmake_type: -Dgtest_force_shared_crt=ON -G "Visual Studio 16 2019" -A x64 23 | build_with: -G "Visual Studio 16 2019" -A x64 24 | - image: ubuntu-latest 25 | cmake_type: -G Ninja 26 | build_with: -G Ninja 27 | - image: macos-latest 28 | cmake_type: -G Ninja 29 | build_with: -G Ninja 30 | fail-fast: false 31 | 32 | steps: 33 | - name: Clone repository 34 | uses: actions/checkout@v2 35 | 36 | - name: Setup Ninja 37 | uses: seanmiddleditch/gha-setup-ninja@v3 38 | 39 | - name: cache-googletest 40 | id: cache-googletest 41 | uses: actions/cache@v2.1.7 42 | with: 43 | path: googletest-install-${{ matrix.image }} 44 | key: cache-googletest-${{ matrix.image }} 45 | 46 | - name: Install googletest 47 | shell: bash 48 | if: steps.cache-googletest.outputs.cache-hit != 'true' 49 | run: | 50 | git clone https://github.com/google/googletest --depth 1 51 | cd googletest 52 | mkdir build && cd build 53 | cmake .. -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_INSTALL_PREFIX='${{ github.workspace }}'/googletest-install-${{ matrix.image }} ${{ matrix.cmake_type }} 54 | cmake --build . --config ${{ env.BUILD_TYPE }} -j 2 55 | cmake --install . --config ${{ env.BUILD_TYPE }} 56 | 57 | - name: Configure with CMake 58 | shell: bash 59 | run: | 60 | mkdir build 61 | cd build 62 | cmake ${{ matrix.build_with }} .. -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DSHUFACV_TEST=ON -DGTest_DIR='${{ github.workspace }}'/googletest-install-${{ matrix.image }}/lib/cmake/GTest 63 | - name: Building 64 | run: cmake --build build --config ${{ env.BUILD_TYPE }} 65 | - name: Testing 66 | working-directory: ${{ github.workspace }}/build 67 | run: ctest -C ${{ env.BUILD_TYPE }} --output-on-failure 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled python 15 | __pycache__ 16 | *.pyc 17 | *.pyd 18 | 19 | # Compiled Dynamic libraries 20 | *.so 21 | *.dylib 22 | *.dll 23 | 24 | # Fortran module files 25 | *.mod 26 | *.smod 27 | 28 | # Compiled Static libraries 29 | *.lai 30 | *.la 31 | *.a 32 | *.lib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | 39 | # Build dir 40 | build/ 41 | 42 | # VSCode 43 | .vscode/ 44 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | # Disable inplace builds to prevent source tree corruption. 4 | if(" ${CMAKE_SOURCE_DIR}" STREQUAL " ${CMAKE_BINARY_DIR}") 5 | message(FATAL_ERROR "FATAL: Building inplace are not allowed. You should create a separate directory for Building.") 6 | endif() 7 | 8 | # Set cmake_install_prefix path 9 | if(NOT DEFINED CMAKE_INSTALL_PREFIX) 10 | set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "Installation Directory") 11 | endif() 12 | message(STATUS "CMAKE_INSTALL_PREFIX = ${CMAKE_INSTALL_PREFIX}") 13 | 14 | if(NOT DEFINED CMAKE_INSTALL_PREFIX) 15 | set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "Installation Directory") 16 | endif() 17 | 18 | 19 | if(NOT DEFINED SHUFACV_VERSION) 20 | string(TIMESTAMP SHUFACV_VERSION "%Y%m%d") 21 | endif() 22 | 23 | set(SHUFACV_VERSION_MAJOR 0) 24 | set(SHUFACV_VERSION_MINOR 0) 25 | #set(SHUFACV_VERSION_PATCH ${SHUFACV_VERSION}) 26 | set(SHUFACV_VERSION_PATCH 0) 27 | set(SHUFACV_VERSION_STRING ${SHUFACV_VERSION_MAJOR}.${SHUFACV_VERSION_MINOR}.${SHUFACV_VERSION_PATCH}) 28 | if(APPLE OR IOS) 29 | # macos / ios only accepts a.b.c.d.e where a=24bit b/c/d/e=10bit 30 | # 20201228 to 20.12.28 31 | set(SHUFACV_VERSION_STRING ${SHUFACV_VERSION_MAJOR}.${SHUFACV_VERSION_MINOR}.${SHUFACV_VERSION_YEAR}.0.0) 32 | endif() 33 | message(STATUS "SHUFACV_VERSION_STRING = ${SHUFACV_VERSION_STRING}") 34 | configure_file(version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h) 35 | 36 | if(CMAKE_TOOLCHAIN_FILE) 37 | # get absolute path, but get_filename_component ABSOLUTE only refer with source dir, so find_file here :( 38 | get_filename_component(CMAKE_TOOLCHAIN_FILE_NAME ${CMAKE_TOOLCHAIN_FILE} NAME) 39 | find_file(CMAKE_TOOLCHAIN_FILE ${CMAKE_TOOLCHAIN_FILE_NAME} PATHS ${CMAKE_SOURCE_DIR} NO_DEFAULT_PATH) 40 | message(STATUS "CMAKE_TOOLCHAIN_FILE = ${CMAKE_TOOLCHAIN_FILE}") 41 | endif() 42 | 43 | if(NOT DEFINED CMAKE_BUILD_TYPE) 44 | set(CMAKE_BUILD_TYPE Release CACHE STRING "Set build type") 45 | endif() 46 | 47 | project(shufacv) 48 | 49 | option(SHUFACV_TEST "Build unit tests?" OFF) 50 | option(SHUFACV_COVERAGE "Build for coverage?" OFF) 51 | 52 | if( (CMAKE_CXX_COMPILER_ID MATCHES "GNU") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) 53 | add_compile_options(-Wextra -Wall -Wno-unused) 54 | endif() 55 | set(CMAKE_CXX_STANDARD 11) 56 | 57 | aux_source_directory(${CMAKE_SOURCE_DIR}/shufaCV SHUFACV_SRCS) 58 | 59 | # Build sfcv shared library 60 | add_library( 61 | ${PROJECT_NAME} 62 | SHARED 63 | ${SHUFACV_SRCS} 64 | ) 65 | target_include_directories( 66 | ${PROJECT_NAME} 67 | PUBLIC ${CMAKE_SOURCE_DIR}/shufaCV ${CMAKE_CURRENT_BINARY_DIR} 68 | ) 69 | # Build sfcv static library 70 | add_library( 71 | ${PROJECT_NAME}_static 72 | STATIC 73 | ${SHUFACV_SRCS} 74 | ) 75 | target_include_directories( 76 | ${PROJECT_NAME}_static 77 | PUBLIC ${CMAKE_SOURCE_DIR}/shufaCV ${CMAKE_CURRENT_BINARY_DIR} 78 | ) 79 | 80 | if(SHUFACV_COVERAGE) 81 | target_compile_options(shufacv_static PUBLIC -coverage -fprofile-arcs -ftest-coverage) 82 | target_link_libraries(shufacv_static PUBLIC -coverage -lgcov) 83 | endif() 84 | 85 | # Report summary 86 | include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/summary.cmake") 87 | 88 | # Add install files 89 | set_target_properties( 90 | ${PROJECT_NAME}_static 91 | PROPERTIES 92 | OUTPUT_NAME ${PROJECT_NAME} 93 | ) 94 | set_target_properties( 95 | ${PROJECT_NAME} 96 | PROPERTIES 97 | SOVERSION ${SHUFACV_VERSION_STRING} 98 | ) 99 | install( 100 | TARGETS ${PROJECT_NAME} 101 | DESTINATION lib 102 | ) 103 | install( 104 | TARGETS ${PROJECT_NAME}_static 105 | DESTINATION lib 106 | ) 107 | 108 | # Unit tests 109 | if(SHUFACV_TEST) 110 | enable_testing() 111 | find_package(GTest REQUIRED) 112 | 113 | macro(shufacv_add_test name) 114 | add_executable(test_${name} tests/test_${name}.cpp) 115 | target_link_libraries(test_${name} PRIVATE shufacv_static GTest::gtest GTest::gtest_main) 116 | target_include_directories(test_${name} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) 117 | gtest_add_tests(TARGET test_${name}) 118 | endmacro() 119 | 120 | shufacv_add_test(mat) 121 | shufacv_add_test(version) 122 | endif() 123 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021-present, ShuFa Community, Inc. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # shufaCV (书法CV) 2 | 3 | [![License](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](.LICENSE) 4 | [![unittest](https://github.com/scarsty/shufaCV/actions/workflows/unit-test.yml/badge.svg)](https://github.com/scarsty/shufaCV/actions/workflows/unit-test.yml) 5 | [![codecov](https://codecov.io/gh/scarsty/shufacv/branch/main/graph/badge.svg)](https://codecov.io/gh/scarsty/shufacv) 6 | ![Widows](https://img.shields.io/badge/Windows-gray?logo=windows&logoColor=blue) 7 | ![Ubuntu](https://img.shields.io/badge/Ubuntu-gray?logo=ubuntu) 8 | ![macOS](https://img.shields.io/badge/-macOS-333333?style=flat&logo=apple) 9 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/scarsty/shufaCV) 10 | 11 | 这是一个由C++书法群发起的,用于淆习和练手的cv库,小目标是超越 [halcon](https://www.mvtec.com/products/halcon) 。长期目标是拳打Torch,脚踢TF。 12 | 13 | QQ 群: 822911263 14 | 15 | ## 授权 16 | 17 | ```bash 18 | 以 BSD 3-Clause License 授权发布。若将其商业应用,我们建议您提交一张书法照到官方 QQ 群。 19 | ``` 20 | -------------------------------------------------------------------------------- /cmake/summary.cmake: -------------------------------------------------------------------------------- 1 | # C/C++ Compilier report 2 | 3 | message(STATUS "") 4 | message(STATUS "") 5 | message(STATUS "Information Summary:") 6 | message(STATUS "") 7 | # CMake information 8 | message(STATUS "CMake information:") 9 | message(STATUS " - CMake version: ${CMAKE_VERSION}") 10 | message(STATUS " - CMake generator: ${CMAKE_GENERATOR}") 11 | message(STATUS " - CMake building tools: ${CMAKE_BUILD_TOOL}") 12 | message(STATUS " - Target System: ${CMAKE_SYSTEM_NAME}") 13 | message(STATUS "") 14 | 15 | 16 | # C/C++ Compilier information 17 | message(STATUS "${PROJECT_NAME} toolchain information:") 18 | message(STATUS " Cross compiling: ${CMAKE_CROSSCOMPILING}") 19 | message(STATUS " C/C++ compilier:") 20 | message(STATUS " - C standard version: C${CMAKE_C_STANDARD}") 21 | message(STATUS " - C standard required: ${CMAKE_C_STANDARD_REQUIRED}") 22 | message(STATUS " - C standard extensions: ${CMAKE_C_EXTENSIONS}") 23 | message(STATUS " - C compilier version: ${CMAKE_C_COMPILER_VERSION}") 24 | message(STATUS " - C compilier: ${CMAKE_C_COMPILER}") 25 | message(STATUS " - C++ standard version: C++${CMAKE_CXX_STANDARD}") 26 | message(STATUS " - C++ standard required: ${CMAKE_CXX_STANDARD_REQUIRED}") 27 | message(STATUS " - C++ standard extensions: ${CMAKE_CXX_EXTENSIONS}") 28 | message(STATUS " - C++ compilier version: ${CMAKE_CXX_COMPILER_VERSION}") 29 | message(STATUS " - C++ compilier: ${CMAKE_CXX_COMPILER}") 30 | message(STATUS " C/C++ compilier flags:") 31 | message(STATUS " - C compilier flags: ${CMAKE_C_FLAGS}") 32 | message(STATUS " - C++ compilier flags: ${CMAKE_CXX_FLAGS}") 33 | message(STATUS " OpenMP:") 34 | if(OpenMP_FOUND) 35 | message(STATUS " - OpenMP was found: YES") 36 | message(STATUS " - OpenMP version: ${OpenMP_C_VERSION}") 37 | else() 38 | message(STATUS " - OpenMP was found: NO") 39 | endif() 40 | message(STATUS "") 41 | 42 | 43 | # CMake project information 44 | message(STATUS "${PROJECT_NAME} building information:") 45 | message(STATUS " - Project source path is: ${PROJECT_SOURCE_DIR}") 46 | message(STATUS " - Project building path is: ${CMAKE_BINARY_DIR}") 47 | message(STATUS "") 48 | 49 | 50 | message(STATUS "${PROJECT_NAME} other information:") 51 | # show building install path 52 | message(STATUS " Package install path: ${CMAKE_INSTALL_PREFIX}") 53 | message(STATUS "") 54 | -------------------------------------------------------------------------------- /shufaCV/Mat.cpp: -------------------------------------------------------------------------------- 1 | #include "Mat.h" 2 | 3 | namespace sfcv { 4 | Mat::Mat(const std::vector& dim) 5 | : data_size_(1), dim_(dim) 6 | { 7 | create(); 8 | } 9 | 10 | Mat::Mat(const std::initializer_list dim) 11 | : data_size_(1), dim_(dim) 12 | { 13 | create(); 14 | } 15 | 16 | void Mat::create() 17 | { 18 | for (const auto d : dim_) 19 | { 20 | data_size_ *= d; 21 | } 22 | if (data_size_ > 0) 23 | { 24 | shared_data_ = std::make_shared >(); 25 | shared_data_->resize(data_size_); 26 | data_ = shared_data_->data(); 27 | } 28 | } 29 | 30 | } // namespace sfcv 31 | -------------------------------------------------------------------------------- /shufaCV/Mat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace sfcv { 7 | 8 | class Mat 9 | { 10 | protected: 11 | int64_t data_size_ = 0; 12 | 13 | std::vector dim_; 14 | 15 | //数据 16 | char* data_ = nullptr; 17 | //共享指针的意义是自动析构不被引用的数据,不可以直接使用 18 | std::shared_ptr > shared_data_; 19 | 20 | void* user_data_ = nullptr; 21 | 22 | void create(); // ctors reuse this 23 | 24 | public: 25 | Mat(const std::vector& dim); 26 | Mat(const std::initializer_list dim); 27 | Mat(int m, int n) 28 | : Mat(std::vector{m, n}) 29 | { 30 | } 31 | Mat() 32 | { 33 | } 34 | 35 | public: 36 | char* data() const 37 | { 38 | return data_; 39 | } 40 | int row() const 41 | { 42 | return col() == 0 ? 0 : data_size_ / col(); 43 | } 44 | int col() const 45 | { 46 | return dim_.size() > 0 ? dim_.back() : 0; 47 | } 48 | int channel() const 49 | { 50 | return 0; 51 | } //unfinished 52 | Mat clone() const; //unfinished 53 | }; 54 | 55 | //运算符重载:+-*数乘 56 | inline const Mat operator+(const Mat& A, const Mat& B) 57 | { 58 | assert((A.row() == B.row()) && (A.col() == B.col())); 59 | Mat R(A.row(), A.col()); 60 | char* da = A.data(); 61 | char* db = B.data(); 62 | char* dr = R.data(); 63 | for (int i = 0; i < A.row(); i++) 64 | { 65 | for (int j = 0; j < A.col(); j++) 66 | { 67 | int idx = i * A.col() + j; 68 | dr[idx] = da[idx] + db[idx]; 69 | } 70 | } 71 | return R; 72 | } 73 | 74 | inline Mat operator-(const Mat& A, const Mat& B) 75 | { 76 | Mat R; 77 | return R; 78 | } 79 | 80 | inline Mat operator*(const Mat& A, const Mat& B) 81 | { 82 | Mat R; 83 | return R; 84 | } 85 | 86 | inline Mat operator*(double r, const Mat& A) 87 | { 88 | Mat R; 89 | return R; 90 | } 91 | 92 | inline Mat operator*(const Mat& A, double r) 93 | { 94 | Mat R; 95 | return R; 96 | } 97 | 98 | }; // namespace sfcv 99 | -------------------------------------------------------------------------------- /tests/test_mat.cpp: -------------------------------------------------------------------------------- 1 | #include "Mat.h" 2 | #include "gtest/gtest.h" 3 | 4 | TEST(Mat, create) 5 | { 6 | constexpr int rows = 3; 7 | constexpr int cols = 5; 8 | 9 | // Mat::Mat(int, int) 10 | sfcv::Mat mat1(rows, cols); 11 | EXPECT_TRUE(mat1.data() != nullptr); 12 | EXPECT_EQ(mat1.row(), rows); 13 | EXPECT_EQ(mat1.col(), cols); 14 | 15 | // Mat::Mat(const std::vector&) 16 | sfcv::Mat mat2(std::vector{rows, cols}); 17 | EXPECT_TRUE(mat2.data() != nullptr); 18 | EXPECT_EQ(mat2.row(), rows); 19 | EXPECT_EQ(mat2.col(), cols); 20 | 21 | // Mat::Mat(std::initializer_list) 22 | sfcv::Mat mat3({rows, cols}); 23 | EXPECT_TRUE(mat3.data() != nullptr); 24 | EXPECT_EQ(mat3.row(), rows); 25 | EXPECT_EQ(mat3.col(), cols); 26 | } 27 | 28 | TEST(Mat, create_const) 29 | { 30 | constexpr int rows = 3; 31 | constexpr int cols = 5; 32 | 33 | const sfcv::Mat mat1(rows, cols); 34 | EXPECT_TRUE(mat1.data() != nullptr); 35 | EXPECT_EQ(mat1.row(), rows); 36 | EXPECT_EQ(mat1.col(), cols); 37 | 38 | const sfcv::Mat mat2(std::vector{rows, cols}); 39 | EXPECT_TRUE(mat2.data() != nullptr); 40 | EXPECT_EQ(mat2.row(), rows); 41 | EXPECT_EQ(mat2.col(), cols); 42 | 43 | sfcv::Mat mat3({rows, cols}); 44 | EXPECT_TRUE(mat3.data() != nullptr); 45 | EXPECT_EQ(mat3.row(), rows); 46 | EXPECT_EQ(mat3.col(), cols); 47 | } 48 | 49 | TEST(Mat, pixel_value_conformance) 50 | { 51 | constexpr int rows = 1; 52 | constexpr int cols = 4; 53 | sfcv::Mat mat(rows, cols); 54 | 55 | char* data = mat.data(); 56 | data[0] = -1; 57 | EXPECT_EQ(data[0], -1); 58 | } 59 | 60 | TEST(Mat, global_add_operator) 61 | { 62 | constexpr int rows = 3; 63 | constexpr int cols = 5; 64 | 65 | const sfcv::Mat mat1(rows, cols); 66 | EXPECT_TRUE(mat1.data() != nullptr); 67 | EXPECT_EQ(mat1.row(), rows); 68 | EXPECT_EQ(mat1.col(), cols); 69 | 70 | const sfcv::Mat mat2(std::vector{rows, cols}); 71 | EXPECT_TRUE(mat2.data() != nullptr); 72 | EXPECT_EQ(mat2.row(), rows); 73 | EXPECT_EQ(mat2.col(), cols); 74 | 75 | char* data1 = mat1.data(); 76 | char* data2 = mat2.data(); 77 | for (int i = 0; i < rows * cols; i++) 78 | { 79 | data1[i] = 1; 80 | data2[i] = 2; 81 | } 82 | sfcv::Mat mat3 = mat1 + mat2; 83 | char* data3 = mat3.data(); 84 | for (int i = 0; i < rows * cols; i++) 85 | { 86 | EXPECT_EQ(data3[i], 3); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /tests/test_version.cpp: -------------------------------------------------------------------------------- 1 | #include "version.h" 2 | #include "gtest/gtest.h" 3 | 4 | TEST(version, macros) 5 | { 6 | EXPECT_TRUE(SHUFACV_VERSION_MAJOR >= 0); 7 | EXPECT_TRUE(SHUFACV_VERSION_MINOR >= 0); 8 | EXPECT_TRUE(SHUFACV_VERSION_PATCH >= 0); 9 | } 10 | -------------------------------------------------------------------------------- /tools/run_clang_format.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | MIT License 4 | 5 | Copyright (c) 2017 Guillaume Papin 6 | 7 | A wrapper script around clang-format, suitable for linting multiple files 8 | and to use for continuous integration. 9 | 10 | This is an alternative API for the clang-format command line. 11 | It runs over multiple files and directories in parallel. 12 | A diff output is produced and a sensible exit code is returned. 13 | """ 14 | 15 | import argparse 16 | import difflib 17 | import errno 18 | import fnmatch 19 | import io 20 | import multiprocessing 21 | import os 22 | import signal 23 | import subprocess 24 | import sys 25 | import traceback 26 | from functools import partial 27 | 28 | try: 29 | from subprocess import DEVNULL # py3k 30 | except ImportError: 31 | DEVNULL = open(os.devnull, "wb") 32 | 33 | 34 | DEFAULT_EXTENSIONS = "c,h,C,H,cpp,hpp,cc,hh,c++,h++,cxx,hxx" 35 | DEFAULT_CLANG_FORMAT_IGNORE = ".clang-format-ignore" 36 | 37 | 38 | class ExitStatus: 39 | SUCCESS = 0 40 | DIFF = 1 41 | TROUBLE = 2 42 | 43 | 44 | def excludes_from_file(ignore_file): 45 | excludes = [] 46 | try: 47 | with io.open(ignore_file, "r", encoding="utf-8") as f: 48 | for line in f: 49 | if line.startswith("#"): 50 | # ignore comments 51 | continue 52 | pattern = line.rstrip() 53 | if not pattern: 54 | # allow empty lines 55 | continue 56 | excludes.append(pattern) 57 | except EnvironmentError as e: 58 | if e.errno != errno.ENOENT: 59 | raise 60 | return excludes 61 | 62 | 63 | def list_files(files, recursive=False, extensions=None, exclude=None): 64 | if extensions is None: 65 | extensions = [] 66 | if exclude is None: 67 | exclude = [] 68 | 69 | out = [] 70 | for file in files: 71 | if recursive and os.path.isdir(file): 72 | for dirpath, dnames, fnames in os.walk(file): 73 | fpaths = [os.path.join(dirpath, fname) for fname in fnames] 74 | for pattern in exclude: 75 | # os.walk() supports trimming down the dnames list 76 | # by modifying it in-place, 77 | # to avoid unnecessary directory listings. 78 | dnames[:] = [x for x in dnames if not fnmatch.fnmatch(os.path.join(dirpath, x), pattern)] 79 | fpaths = [x for x in fpaths if not fnmatch.fnmatch(x, pattern)] 80 | for f in fpaths: 81 | ext = os.path.splitext(f)[1][1:] 82 | if ext in extensions: 83 | out.append(f) 84 | else: 85 | out.append(file) 86 | return out 87 | 88 | 89 | def make_diff(file, original, reformatted): 90 | return list( 91 | difflib.unified_diff( 92 | original, 93 | reformatted, 94 | fromfile=f"{file}\t(original)", 95 | tofile=f"{file}\t(reformatted)", 96 | n=3, 97 | ) 98 | ) 99 | 100 | 101 | class DiffError(Exception): 102 | def __init__(self, message, errs=None): 103 | super(DiffError, self).__init__(message) 104 | self.errs = errs or [] 105 | 106 | 107 | class UnexpectedError(Exception): 108 | def __init__(self, message, exc=None): 109 | super(UnexpectedError, self).__init__(message) 110 | self.formatted_traceback = traceback.format_exc() 111 | self.exc = exc 112 | 113 | 114 | def run_clang_format_diff_wrapper(args, file): 115 | try: 116 | ret = run_clang_format_diff(args, file) 117 | return ret 118 | except DiffError: 119 | raise 120 | except Exception as e: 121 | raise UnexpectedError(f"{file}: {e.__class__.__name__}: {e}", e) 122 | 123 | 124 | def run_clang_format_diff(args, file): 125 | try: 126 | with io.open(file, "r", encoding="utf-8") as f: 127 | original = f.readlines() 128 | except IOError as exc: 129 | raise DiffError(str(exc)) 130 | 131 | if args.fix: 132 | invocation = [args.clang_format_executable, "-i", file] 133 | else: 134 | invocation = [args.clang_format_executable, file] 135 | 136 | if args.style: 137 | invocation.extend(["--style", args.style]) 138 | 139 | if args.dry_run: 140 | print(" ".join(invocation)) 141 | return [], [] 142 | 143 | # Use of utf-8 to decode the process output. 144 | # 145 | # Hopefully, this is the correct thing to do. 146 | # 147 | # It's done due to the following assumptions (which may be incorrect): 148 | # - clang-format will returns the bytes read from the files as-is, 149 | # without conversion, and it is already assumed that the files use utf-8. 150 | # - if the diagnostics were internationalized, they would use utf-8: 151 | # > Adding Translations to Clang 152 | # > 153 | # > Not possible yet! 154 | # > Diagnostic strings should be written in UTF-8, 155 | # > the client can translate to the relevant code page if needed. 156 | # > Each translation completely replaces the format string 157 | # > for the diagnostic. 158 | # > -- http://clang.llvm.org/docs/InternalsManual.html#internals-diag-translation 159 | # 160 | # It's not pretty, due to Python 2 & 3 compatibility. 161 | encoding_py3 = {"encoding": "utf-8"} 162 | 163 | try: 164 | proc = subprocess.Popen( 165 | invocation, 166 | stdout=subprocess.PIPE, 167 | stderr=subprocess.PIPE, 168 | universal_newlines=True, 169 | **encoding_py3, 170 | ) 171 | except OSError as exc: 172 | raise DiffError(f"Command '{subprocess.list2cmdline(invocation)}' failed to start: {exc}") 173 | proc_stdout = proc.stdout 174 | proc_stderr = proc.stderr 175 | 176 | # hopefully the stderr pipe won't get full and block the process 177 | outs = list(proc_stdout.readlines()) 178 | errs = list(proc_stderr.readlines()) 179 | proc.wait() 180 | if proc.returncode: 181 | raise DiffError( 182 | f"Command '{subprocess.list2cmdline(invocation)}' returned " 183 | f"non-zero exit status {proc.returncode}", 184 | errs, 185 | ) 186 | if args.fix: 187 | return [], errs 188 | return make_diff(file, original, outs), errs 189 | 190 | 191 | def bold_red(s): 192 | return "\x1b[1m\x1b[31m" + s + "\x1b[0m" 193 | 194 | 195 | def colorize(diff_lines): 196 | def bold(s): 197 | return "\x1b[1m" + s + "\x1b[0m" 198 | 199 | def cyan(s): 200 | return "\x1b[36m" + s + "\x1b[0m" 201 | 202 | def green(s): 203 | return "\x1b[32m" + s + "\x1b[0m" 204 | 205 | def red(s): 206 | return "\x1b[31m" + s + "\x1b[0m" 207 | 208 | for line in diff_lines: 209 | if line[:4] in ["--- ", "+++ "]: 210 | yield bold(line) 211 | elif line.startswith("@@ "): 212 | yield cyan(line) 213 | elif line.startswith("+"): 214 | yield green(line) 215 | elif line.startswith("-"): 216 | yield red(line) 217 | else: 218 | yield line 219 | 220 | 221 | def print_diff(diff_lines, use_color): 222 | if use_color: 223 | diff_lines = colorize(diff_lines) 224 | sys.stdout.writelines(diff_lines) 225 | 226 | 227 | def print_trouble(prog, message, use_colors): 228 | error_text = "error:" 229 | if use_colors: 230 | error_text = bold_red(error_text) 231 | print(f"{prog}: {error_text} {message}", file=sys.stderr) 232 | 233 | 234 | def main(): 235 | parser = argparse.ArgumentParser(description=__doc__) 236 | parser.add_argument( 237 | "--clang_format_executable", 238 | metavar="EXECUTABLE", 239 | help="path to the clang-format executable", 240 | default="clang-format", 241 | ) 242 | parser.add_argument( 243 | "--extensions", 244 | help=f"comma separated list of file extensions (default: {DEFAULT_EXTENSIONS})", 245 | default=DEFAULT_EXTENSIONS, 246 | ) 247 | parser.add_argument("--recursive", action="store_true", help="run recursively over directories") 248 | parser.add_argument("--dry_run", action="store_true", help="just print the list of files") 249 | parser.add_argument("--fix", action="store_true", help="format file instead of printing differences") 250 | parser.add_argument("--files", metavar="file", nargs="+") 251 | parser.add_argument("--quiet", action="store_true", help="disable output, useful for the exit code") 252 | parser.add_argument( 253 | "-j", 254 | metavar="N", 255 | type=int, 256 | default=0, 257 | help="run N clang-format jobs in parallel" " (default number of cpus + 1)", 258 | ) 259 | parser.add_argument( 260 | "--color", 261 | default="auto", 262 | choices=["auto", "always", "never"], 263 | help="show colored diff (default: auto)", 264 | ) 265 | parser.add_argument( 266 | "--exclude", 267 | metavar="PATTERN", 268 | action="append", 269 | default=[], 270 | help="exclude paths matching the given glob-like pattern(s)" " from recursive search", 271 | ) 272 | parser.add_argument("--style", help="formatting style to apply (LLVM, Google, Chromium, Mozilla, WebKit)") 273 | 274 | args = parser.parse_args() 275 | 276 | # use default signal handling, like diff return SIGINT value on ^C 277 | # https://bugs.python.org/issue14229#msg156446 278 | signal.signal(signal.SIGINT, signal.SIG_DFL) 279 | try: 280 | signal.SIGPIPE 281 | except AttributeError: 282 | # compatibility, SIGPIPE does not exist on Windows 283 | pass 284 | else: 285 | signal.signal(signal.SIGPIPE, signal.SIG_DFL) 286 | 287 | colored_stdout = False 288 | colored_stderr = False 289 | if args.color == "always": 290 | colored_stdout = True 291 | colored_stderr = True 292 | elif args.color == "auto": 293 | colored_stdout = sys.stdout.isatty() 294 | colored_stderr = sys.stderr.isatty() 295 | 296 | version_invocation = [args.clang_format_executable, str("--version")] 297 | try: 298 | subprocess.check_call(version_invocation, stdout=DEVNULL) 299 | except subprocess.CalledProcessError as e: 300 | print_trouble(parser.prog, str(e), use_colors=colored_stderr) 301 | return ExitStatus.TROUBLE 302 | except OSError as e: 303 | print_trouble( 304 | parser.prog, 305 | f"Command '{subprocess.list2cmdline(version_invocation)}' failed to start: {e}", 306 | use_colors=colored_stderr, 307 | ) 308 | return ExitStatus.TROUBLE 309 | 310 | retcode = ExitStatus.SUCCESS 311 | 312 | excludes = excludes_from_file(DEFAULT_CLANG_FORMAT_IGNORE) 313 | excludes.extend(args.exclude) 314 | 315 | files = list_files( 316 | args.files, recursive=args.recursive, exclude=excludes, extensions=args.extensions.split(",") 317 | ) 318 | 319 | if not files: 320 | return 321 | 322 | njobs = args.j 323 | if njobs == 0: 324 | njobs = multiprocessing.cpu_count() + 1 325 | njobs = min(len(files), njobs) 326 | 327 | if njobs == 1: 328 | # execute directly instead of in a pool, 329 | # less overhead, simpler stacktraces 330 | it = (run_clang_format_diff_wrapper(args, file) for file in files) 331 | pool = None 332 | else: 333 | pool = multiprocessing.Pool(njobs) 334 | it = pool.imap_unordered(partial(run_clang_format_diff_wrapper, args), files) 335 | pool.close() 336 | while True: 337 | try: 338 | outs, errs = next(it) 339 | except StopIteration: 340 | break 341 | except DiffError as e: 342 | print_trouble(parser.prog, str(e), use_colors=colored_stderr) 343 | retcode = ExitStatus.TROUBLE 344 | sys.stderr.writelines(e.errs) 345 | except UnexpectedError as e: 346 | print_trouble(parser.prog, str(e), use_colors=colored_stderr) 347 | sys.stderr.write(e.formatted_traceback) 348 | retcode = ExitStatus.TROUBLE 349 | # stop at the first unexpected error, 350 | # something could be very wrong, 351 | # don't process all files unnecessarily 352 | if pool: 353 | pool.terminate() 354 | break 355 | else: 356 | sys.stderr.writelines(errs) 357 | if outs == []: 358 | continue 359 | if not args.quiet: 360 | print_diff(outs, use_color=colored_stdout) 361 | if retcode == ExitStatus.SUCCESS: 362 | retcode = ExitStatus.DIFF 363 | if pool: 364 | pool.join() 365 | return retcode 366 | 367 | 368 | if __name__ == "__main__": 369 | sys.exit(main()) 370 | -------------------------------------------------------------------------------- /version.h.in: -------------------------------------------------------------------------------- 1 | #define SHUFACV_VERSION_MAJOR @SHUFACV_VERSION_MAJOR@ 2 | #define SHUFACV_VERSION_MINOR @SHUFACV_VERSION_MINOR@ 3 | #define SHUFACV_VERSION_PATCH @SHUFACV_VERSION_PATCH@ 4 | --------------------------------------------------------------------------------