From 76dacea97cc394ab6d57ff6e14f4b7b74ef152a9 Mon Sep 17 00:00:00 2001 From: Arijit Dasgupta Date: Sun, 9 Nov 2025 07:09:57 +0000 Subject: [PATCH 01/12] linting and formatting support for both python and C++ --- .clang-format | 119 ++++++++++++++++++++++ .clang-tidy | 37 +++++++ CMakeLists.txt | 3 + README.md | 34 +++++++ genmetaballs/src/cuda/bindings.cu | 9 +- genmetaballs/src/cuda/core/add.cuh | 16 +-- genmetaballs/src/cuda/core/utils.cu | 6 +- genmetaballs/src/genmetaballs/gpu_add.py | 1 + pixi.lock | 122 ++++++++++++++++++++++- pyproject.toml | 50 ++++++++++ scripts/format/all.sh | 36 +++++++ scripts/format/check-all.sh | 39 ++++++++ scripts/format/cpp.sh | 95 ++++++++++++++++++ scripts/format/python.sh | 104 +++++++++++++++++++ scripts/lint/all.sh | 39 ++++++++ scripts/lint/cpp-fix.sh | 48 +++++++++ scripts/lint/cpp.sh | 122 +++++++++++++++++++++++ scripts/lint/fix-all.sh | 36 +++++++ scripts/lint/python-fix.sh | 42 ++++++++ scripts/lint/python.sh | 110 ++++++++++++++++++++ tests/test_add.cu | 12 +-- tests/test_gpu_add.py | 2 +- 22 files changed, 1051 insertions(+), 31 deletions(-) create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100755 scripts/format/all.sh create mode 100755 scripts/format/check-all.sh create mode 100755 scripts/format/cpp.sh create mode 100755 scripts/format/python.sh create mode 100755 scripts/lint/all.sh create mode 100755 scripts/lint/cpp-fix.sh create mode 100755 scripts/lint/cpp.sh create mode 100755 scripts/lint/fix-all.sh create mode 100755 scripts/lint/python-fix.sh create mode 100755 scripts/lint/python.sh diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..48bf157 --- /dev/null +++ b/.clang-format @@ -0,0 +1,119 @@ +--- +Language: Cpp +Standard: c++20 +BasedOnStyle: LLVM +IndentWidth: 4 +TabWidth: 4 +UseTab: Never +ColumnLimit: 100 +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^<.*\.(cu|cu\.h|cuh|hpp|h)$' + Priority: 1 + - Regex: '^<.*\.(cpp|cc|c\+\+|cxx|c)$' + Priority: 2 + - Regex: '^<.*' + Priority: 3 + - Regex: '.*' + Priority: 4 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: true +IndentPPDirectives: None +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +... + diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..0b573bd --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,37 @@ +--- +# Clang-tidy configuration for CUDA/C++ project +Checks: > + -*, + bugprone-*, + cert-*, + cppcoreguidelines-*, + performance-*, + readability-*, + modernize-*, + -bugprone-easily-swappable-parameters, + -readability-identifier-length, + -readability-identifier-naming, + -readability-named-parameter, + -readability-implicit-bool-conversion, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-pro-bounds-pointer-arithmetic, + -cppcoreguidelines-pro-type-union-access, + -cppcoreguidelines-pro-type-vararg, + -cppcoreguidelines-owning-memory, + -modernize-use-trailing-return-type, + -readability-magic-numbers + +WarningsAsErrors: '' +HeaderFilterRegex: '.*' +FormatStyle: 'file' +CheckOptions: + - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor + value: true + - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions + value: true + - key: performance-unnecessary-value-param.AllowedTypes + value: 'std::vector;.*Iterator' + - key: modernize-use-nodiscard.Macros + value: 'CUDA_CHECK' + diff --git a/CMakeLists.txt b/CMakeLists.txt index 3520ea8..6fa89b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,9 @@ check_language(CUDA) set(CMAKE_CXX_STANDARD 20) +# Generate compile_commands.json for clang-tidy and other tools +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + ################ # Core Library # ################ diff --git a/README.md b/README.md index 31667af..4b903eb 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,37 @@ To run both C++/CUDA and Python tests together: ```bash pixi run test ``` + +## Formatting & Linting + +### Formatting + +Format all files (C++/CUDA + Python): +```bash +pixi run format +``` + +Check formatting without modifying files: +```bash +pixi run format-check +``` + +### Linting + +Lint all files: +```bash +pixi run lint +``` + +Auto-fix linting issues: +```bash +pixi run lint-fix +``` + +**Language-specific commands:** +- `format-cpp`, `format-python` - Format specific language +- `format-check-cpp`, `format-check-python` - Check formatting for specific language +- `lint-cpp`, `lint-python` - Lint specific language +- `lint-cpp-fix`, `lint-python-fix` - Auto-fix linting issues for specific language +- `lint-full` - C++/CUDA linting with compile_commands.json (more accurate) +- `lint-full-fix` - Auto-fix C++/CUDA issues using compile_commands.json diff --git a/genmetaballs/src/cuda/bindings.cu b/genmetaballs/src/cuda/bindings.cu index f32e6a6..6354aa2 100644 --- a/genmetaballs/src/cuda/bindings.cu +++ b/genmetaballs/src/cuda/bindings.cu @@ -1,5 +1,4 @@ #include - #include #include @@ -11,10 +10,6 @@ constexpr uint32_t BLOCK_DIM = 1024; namespace nb = nanobind; NB_MODULE(_genmetaballs_bindings, m) { - m.def( - "gpu_add", - &gpu_add, - "Add two lists elementwise on the GPU", - nb::arg("a"), nb::arg("b") - ); + m.def("gpu_add", &gpu_add, "Add two lists elementwise on the GPU", + nb::arg("a"), nb::arg("b")); } diff --git a/genmetaballs/src/cuda/core/add.cuh b/genmetaballs/src/cuda/core/add.cuh index 20fc703..9a068b1 100644 --- a/genmetaballs/src/cuda/core/add.cuh +++ b/genmetaballs/src/cuda/core/add.cuh @@ -4,22 +4,14 @@ #include "utils.h" -__global__ void add_kernel( - float const *a, - float const *b, - const uint32_t n, - float *sum -) { +__global__ void add_kernel(float const* a, float const* b, const uint32_t n, float* sum) { const uint32_t i = threadIdx.x + blockIdx.x * blockDim.x; - if(i < n) + if (i < n) sum[i] = a[i] + b[i]; } -template -std::vector gpu_add( - const std::vector &a_vec, - const std::vector &b_vec -) { +template +std::vector gpu_add(const std::vector& a_vec, const std::vector& b_vec) { const uint32_t n = a_vec.size(); const uint32_t nbytes = n * sizeof(float); float *a, *b, *sum; diff --git a/genmetaballs/src/cuda/core/utils.cu b/genmetaballs/src/cuda/core/utils.cu index 5643c32..022a023 100644 --- a/genmetaballs/src/cuda/core/utils.cu +++ b/genmetaballs/src/cuda/core/utils.cu @@ -4,10 +4,10 @@ #include "utils.h" -void cuda_check(cudaError_t code, const char *file, int line) { +void cuda_check(cudaError_t code, const char* file, int line) { if (code != cudaSuccess) { - std::cerr << "CUDA error at " << file << ":" << line << ": " - << cudaGetErrorString(code) << std::endl; + std::cerr << "CUDA error at " << file << ":" << line << ": " << cudaGetErrorString(code) + << std::endl; exit(1); } } diff --git a/genmetaballs/src/genmetaballs/gpu_add.py b/genmetaballs/src/genmetaballs/gpu_add.py index ca105a4..214b75f 100644 --- a/genmetaballs/src/genmetaballs/gpu_add.py +++ b/genmetaballs/src/genmetaballs/gpu_add.py @@ -1,4 +1,5 @@ from . import _genmetaballs_bindings as _gmbb + def gpu_add(a: list[float], b: list[float]) -> list[float]: return _gmbb.gpu_add(a, b) diff --git a/pixi.lock b/pixi.lock index 6776bcb..5369d14 100644 --- a/pixi.lock +++ b/pixi.lock @@ -18,6 +18,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.5-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-compiler-1.11.0-h4d9bdce_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/clang-format-19-19.1.7-default_h99862b1_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/clang-format-19.1.7-default_h99862b1_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/clang-tools-19.1.7-default_h99862b1_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cmake-4.1.2-hc85cc9f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/conda-gcc-specs-14.3.0-hb991d5c_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cuda-cccl_linux-64-12.8.90-ha770c72_1.conda @@ -94,6 +97,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-h1aa0949_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcap-2.76-h0b2e76d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp19.1-19.1.7-default_h99862b1_5.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.5-default_h746c552_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcublas-12.8.4.1-h9ab20c4_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcublas-dev-12.8.4.1-h9ab20c4_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcufft-11.3.3.83-h5888daf_1.conda @@ -122,6 +127,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-h767d61c_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgpg-error-1.55-h3f2d84a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.7-hf7376ad_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.5-hf7376ad_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda @@ -207,6 +214,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ee/79/6ad4dda2cfd55e41ac9ed6d73ef9ab9475b1eef69f3a85957210c74ba12c/ruff-0.14.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: ./ packages: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 @@ -327,6 +335,55 @@ packages: purls: [] size: 155907 timestamp: 1759649036195 +- conda: https://conda.anaconda.org/conda-forge/linux-64/clang-format-19.1.7-default_h99862b1_5.conda + sha256: 118af30168d24a08c6e6443c269bcef5369262003e7dda227decea14b73534b4 + md5: 572bc6b9b35539a2e152eb25987c1558 + depends: + - __glibc >=2.17,<3.0.a0 + - clang-format-19 19.1.7 default_h99862b1_5 + - libclang-cpp19.1 >=19.1.7,<19.2.0a0 + - libgcc >=14 + - libllvm19 >=19.1.7,<19.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 24659 + timestamp: 1759437766287 +- conda: https://conda.anaconda.org/conda-forge/linux-64/clang-format-19-19.1.7-default_h99862b1_5.conda + sha256: d57c3c07f575354ccb6c9f88c591e6897e87722ea7e02a82763f932541411519 + md5: a54ce63e21140927f7dbecabc1fe18ff + depends: + - __glibc >=2.17,<3.0.a0 + - libclang-cpp19.1 >=19.1.7,<19.2.0a0 + - libgcc >=14 + - libllvm19 >=19.1.7,<19.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 67991 + timestamp: 1759437723202 +- conda: https://conda.anaconda.org/conda-forge/linux-64/clang-tools-19.1.7-default_h99862b1_5.conda + sha256: 43914dbded3d3d1060901febc466f7bcbb93d08aa91cd6991b37188fff491f27 + md5: a82ea712cc8059894be5aba7779430aa + depends: + - __glibc >=2.17,<3.0.a0 + - clang-format 19.1.7 default_h99862b1_5 + - libclang-cpp19.1 >=19.1.7,<19.2.0a0 + - libclang13 >=19.1.7 + - libgcc >=14 + - libllvm19 >=19.1.7,<19.2.0a0 + - libstdcxx >=14 + - libxml2 + - libxml2-16 >=2.14.6 + constrains: + - clangdev 19.1.7 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 21638158 + timestamp: 1759437805596 - conda: https://conda.anaconda.org/conda-forge/linux-64/cmake-4.1.2-hc85cc9f_0.conda sha256: 2176c4bce9f602cee0efbae86283a1a75733921ecc0916c8d2f49df2aee1a0f0 md5: 3d5d0a07f07ba1fc43f52b5e33e3cd7c @@ -1104,7 +1161,7 @@ packages: - pypi: ./ name: genmetaballs version: 0.0.1 - sha256: 8c7c8aa8e51cf9037eabcd68fe0374cfe24177e34d8e2e7291e9854dd1d16fd9 + sha256: 4640ace5bf014126fce6f3f949d3d4e312cb04fa371fe80b6f95274e7eb84ee6 requires_dist: - numpy requires_python: '>=3.13' @@ -1285,6 +1342,32 @@ packages: purls: [] size: 121852 timestamp: 1744577167992 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp19.1-19.1.7-default_h99862b1_5.conda + sha256: 1aeacc24c49355992968452ab209fcf91a638ce550290d6528f0d93c455873d5 + md5: 68285505efe44eded837be21deec1614 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libllvm19 >=19.1.7,<19.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 20849192 + timestamp: 1759437442452 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.5-default_h746c552_1.conda + sha256: 070871a19d7a1bc750284721a1f722c527ef466b1461e0de84abbdbb755f4221 + md5: dd39147d65f5edf3b3ebb06f5a0ef43e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libllvm21 >=21.1.5,<21.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 12340532 + timestamp: 1762471521823 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcublas-12.8.4.1-h9ab20c4_1.conda sha256: 3d3f7344db000feced2f9154cf0b3f3d245a1d317a1981e43b8b15f7baaaf6f1 md5: 3ba4fd8bef181c020173d29ac67cae68 @@ -1645,6 +1728,38 @@ packages: purls: [] size: 790176 timestamp: 1754908768807 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.7-hf7376ad_2.conda + sha256: 8310e3bf47af086ef9d4434d69139c888e2c13b66d8815d6dc5e7c272a267538 + md5: 8131707c4e4fac60e6e7da4d532484a4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libxml2 + - libxml2-16 >=2.14.5 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 41033274 + timestamp: 1757357153329 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.5-hf7376ad_0.conda + sha256: 180d77016c2eb5c8722f31a4750496b773e810529110d370ffc6d0cbbf6d15bb + md5: 9d476d7712c3c78ace006017c83d3889 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - libxml2 + - libxml2-16 >=2.14.6 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + purls: [] + size: 44350262 + timestamp: 1762289424598 - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda sha256: f2591c0069447bbe28d4d696b7fcb0c5bd0b4ac582769b89addbcf26fb3430d8 md5: 1a580f7796c7bf6393fddb8bbbde58dc @@ -2363,6 +2478,11 @@ packages: purls: [] size: 193775 timestamp: 1748644872902 +- pypi: https://files.pythonhosted.org/packages/ee/79/6ad4dda2cfd55e41ac9ed6d73ef9ab9475b1eef69f3a85957210c74ba12c/ruff-0.14.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: ruff + version: 0.14.4 + sha256: 456daa2fa1021bc86ca857f43fe29d5d8b3f0e55e9f90c58c317c1dcc2afc7b5 + requires_python: '>=3.7' - conda: https://conda.anaconda.org/conda-forge/noarch/scikit-build-core-0.11.6-pyh7e86bf3_1.conda sha256: bf802baa1a770c0ca631624a3627f84cae7978b56da02826b37ff1c5852a720a md5: 4965b07e5cae94005cbe759e39f3def1 diff --git a/pyproject.toml b/pyproject.toml index 9f89fef..e48f6d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,14 +29,36 @@ ninja = "*" scikit-build-core = "*" nanobind = "*" gtest = ">=1.17.0,<2" +clang-tools = ">=18,<20" # Includes clang-format and clang-tidy [tool.pixi.pypi-dependencies] genmetaballs = { path = ".", editable = true } +ruff = ">=0.8.0" [tool.pixi.tasks] ctest = "ctest --test-dir build" pytest = "pytest" test = { depends-on = ["ctest", "pytest"] } +compile-commands = "cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON" +# Combined formatting and linting (C++/CUDA + Python) +format = { cmd = "scripts/format/all.sh" } +format-check = { cmd = "scripts/format/check-all.sh" } +lint = { cmd = "scripts/lint/all.sh" } +lint-fix = { cmd = "scripts/lint/fix-all.sh" } +# C++/CUDA only +format-cpp = { cmd = "scripts/format/cpp.sh" } +format-check-cpp = { cmd = "scripts/format/cpp.sh check" } +lint-cpp = { cmd = "scripts/lint/cpp.sh" } +lint-cpp-fix = { cmd = "scripts/lint/cpp-fix.sh" } +# lint-full: Uses compile_commands.json for more accurate C++/CUDA linting +# (includes proper include paths, defines, and compiler flags from CMake) +lint-full = { cmd = "scripts/lint/cpp.sh full" } +lint-full-fix = { cmd = "scripts/lint/cpp-fix.sh full" } +# Python only +format-python = { cmd = "scripts/format/python.sh" } +format-check-python = { cmd = "scripts/format/python.sh check" } +lint-python = { cmd = "scripts/lint/python.sh" } +lint-python-fix = { cmd = "scripts/lint/python-fix.sh" } [tool.pixi.environments] default = { solve-group = "default", features = ["dev"] } @@ -53,3 +75,31 @@ dev = ["pytest"] [tool.pytest.ini_options] addopts = ["--import-mode=importlib", "--doctest-modules"] testpaths = ["tests", "src"] + +[tool.ruff] +line-length = 100 +target-version = "py313" + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +skip-magic-trailing-comma = false +line-ending = "auto" + +[tool.ruff.lint] +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # pyflakes + "I", # isort + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "UP", # pyupgrade +] +ignore = [ + "E501", # line too long (handled by formatter) + "B008", # do not perform function calls in argument defaults +] + +[tool.ruff.lint.isort] +known-first-party = ["genmetaballs"] diff --git a/scripts/format/all.sh b/scripts/format/all.sh new file mode 100755 index 0000000..6196c45 --- /dev/null +++ b/scripts/format/all.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set -e + +echo "๐Ÿ”ง Formatting all files (C++/CUDA + Python)..." +echo "" + +# Format C++/CUDA files +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +if [ -f "scripts/format/cpp.sh" ]; then + scripts/format/cpp.sh + CPP_EXIT=$? +else + echo "โš ๏ธ C++/CUDA format script not found" + CPP_EXIT=0 +fi + +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + +# Format Python files +if [ -f "scripts/format/python.sh" ]; then + scripts/format/python.sh + PYTHON_EXIT=$? +else + echo "โš ๏ธ Python format script not found" + PYTHON_EXIT=0 +fi + +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +echo "โœจ Formatting complete!" + +if [ $CPP_EXIT -ne 0 ] || [ $PYTHON_EXIT -ne 0 ]; then + exit 1 +fi + diff --git a/scripts/format/check-all.sh b/scripts/format/check-all.sh new file mode 100755 index 0000000..22596ea --- /dev/null +++ b/scripts/format/check-all.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -e + +echo "๐Ÿ” Checking formatting for all files (C++/CUDA + Python)..." +echo "" + +# Check C++/CUDA files +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +if [ -f "scripts/format/cpp.sh" ]; then + scripts/format/cpp.sh check + CPP_EXIT=$? +else + echo "โš ๏ธ C++/CUDA format script not found" + CPP_EXIT=0 +fi + +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + +# Check Python files +if [ -f "scripts/format/python.sh" ]; then + scripts/format/python.sh check + PYTHON_EXIT=$? +else + echo "โš ๏ธ Python format script not found" + PYTHON_EXIT=0 +fi + +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + +if [ $CPP_EXIT -eq 0 ] && [ $PYTHON_EXIT -eq 0 ]; then + echo "โœจ All files are properly formatted!" + exit 0 +else + echo "โŒ Some files need formatting. Run 'pixi run format' to fix." + exit 1 +fi + diff --git a/scripts/format/cpp.sh b/scripts/format/cpp.sh new file mode 100755 index 0000000..1c37cfe --- /dev/null +++ b/scripts/format/cpp.sh @@ -0,0 +1,95 @@ +#!/bin/bash +set -e + +FILES=$(find genmetaballs/src/cuda tests -type f \( -name '*.cu' -o -name '*.cuh' \) 2>/dev/null || true) + +if [ -z "$FILES" ]; then + echo "โœ“ No CUDA/C++ files found" + exit 0 +fi + +if [ "$1" = "check" ]; then + echo "๐Ÿ” Checking CUDA/C++ file formatting..." + NEEDS_FORMAT=0 + OLD_IFS="$IFS" + IFS=$'\n' + for file in $FILES; do + IFS="$OLD_IFS" + if [ -f "$file" ]; then + # Check formatting: capture both stdout and stderr for detailed error messages + set +e # Temporarily disable exit on error to capture exit code + ERROR_OUTPUT=$(clang-format --dry-run --Werror "$file" 2>&1) + EXIT_CODE=$? + set -e # Re-enable exit on error + if [ $EXIT_CODE -eq 0 ]; then + echo " โœ“ $file" + else + echo " โœ— $file needs formatting" + # Show full error output with file and line details + if [ -n "$ERROR_OUTPUT" ]; then + echo "$ERROR_OUTPUT" | sed 's/^/ /' + else + echo " File does not match clang-format style" + fi + NEEDS_FORMAT=1 + fi + fi + done + IFS="$OLD_IFS" + + if [ $NEEDS_FORMAT -eq 0 ]; then + echo "โœจ All CUDA/C++ files are properly formatted!" + exit 0 + else + echo "โŒ Some files need formatting. Run 'pixi run format' to fix." + exit 1 + fi +else + echo "๐Ÿ”ง Formatting CUDA/C++ files..." + FORMATTED=0 + ALREADY_FORMATTED=0 + OLD_IFS="$IFS" + IFS=$'\n' + for file in $FILES; do + IFS="$OLD_IFS" + if [ -f "$file" ]; then + # Check if file needs formatting + set +e + clang-format --dry-run --Werror "$file" >/dev/null 2>&1 + CHECK_EXIT=$? + set -e + if [ $CHECK_EXIT -eq 0 ]; then + echo " โœ“ Already formatted: $file" + ALREADY_FORMATTED=1 + else + set +e + ERROR_OUTPUT=$(clang-format -i "$file" 2>&1) + EXIT_CODE=$? + set -e + if [ $EXIT_CODE -eq 0 ]; then + echo " โœ“ Formatted: $file" + FORMATTED=1 + else + echo " โœ— Failed to format: $file" + # Show full error output with file and line details + if [ -n "$ERROR_OUTPUT" ]; then + echo "$ERROR_OUTPUT" | sed 's/^/ /' + else + echo " Unknown formatting error" + fi + echo "" + echo "๐Ÿ’ก Review the error above and fix the issues, then run 'pixi run format' again" + exit 1 + fi + fi + fi + done + IFS="$OLD_IFS" + + if [ $FORMATTED -eq 1 ]; then + echo "โœจ Formatting complete!" + elif [ $ALREADY_FORMATTED -eq 1 ]; then + echo "โœจ All CUDA/C++ files were already formatted!" + fi +fi + diff --git a/scripts/format/python.sh b/scripts/format/python.sh new file mode 100755 index 0000000..c02a8f4 --- /dev/null +++ b/scripts/format/python.sh @@ -0,0 +1,104 @@ +#!/bin/bash +set -e + +PYTHON_FILES=$(find genmetaballs tests -type f -name '*.py' 2>/dev/null | sort || true) + +if [ -z "$PYTHON_FILES" ]; then + echo "โœ“ No Python files found" + exit 0 +fi + +if [ "$1" = "check" ]; then + echo "๐Ÿ” Checking Python file formatting..." + NEEDS_FORMAT=0 + + # First check import ordering + ruff check --select I --fix --quiet $PYTHON_FILES >/dev/null 2>&1 || true + + # Then check formatting + OLD_IFS="$IFS" + IFS=$'\n' + for file in $PYTHON_FILES; do + IFS="$OLD_IFS" + if [ -f "$file" ]; then + set +e + ERROR_OUTPUT=$(ruff format --check "$file" 2>&1) + EXIT_CODE=$? + set -e + if [ $EXIT_CODE -eq 0 ]; then + echo " โœ“ $file" + else + echo " โœ— $file needs formatting" + if [ -n "$ERROR_OUTPUT" ]; then + echo "$ERROR_OUTPUT" | sed 's/^/ /' + else + echo " File does not match ruff format style" + fi + NEEDS_FORMAT=1 + fi + fi + done + IFS="$OLD_IFS" + + if [ $NEEDS_FORMAT -eq 0 ]; then + echo "โœจ All Python files are properly formatted!" + exit 0 + else + echo "โŒ Some Python files need formatting. Run 'pixi run format' to fix." + exit 1 + fi +else + echo "๐Ÿ”ง Formatting Python files..." + FORMATTED=0 + ALREADY_FORMATTED=0 + + OLD_IFS="$IFS" + IFS=$'\n' + for file in $PYTHON_FILES; do + IFS="$OLD_IFS" + if [ -f "$file" ]; then + # Check if file needs formatting + set +e + ruff format --check "$file" >/dev/null 2>&1 + NEEDS_FORMAT=$? + set -e + + # Fix import ordering first (this might change the file) + ruff check --select I --fix --quiet "$file" >/dev/null 2>&1 || true + + # Then format if needed + if [ $NEEDS_FORMAT -ne 0 ]; then + set +e + ERROR_OUTPUT=$(ruff format "$file" 2>&1) + EXIT_CODE=$? + set -e + if [ $EXIT_CODE -eq 0 ]; then + echo " โœ“ Formatted: $file" + FORMATTED=1 + else + echo " โœ— Failed to format: $file" + # Show full error output with file and line details + if [ -n "$ERROR_OUTPUT" ]; then + echo "$ERROR_OUTPUT" | sed 's/^/ /' + else + echo " Unknown formatting error" + fi + echo "" + echo "๐Ÿ’ก Review the error above and fix the issues, then run 'pixi run format' again" + exit 1 + fi + else + echo " โœ“ Already formatted: $file" + ALREADY_FORMATTED=1 + fi + fi + done + IFS="$OLD_IFS" + + if [ $FORMATTED -eq 1 ]; then + echo "โœจ Python formatting complete!" + elif [ $ALREADY_FORMATTED -eq 1 ]; then + echo "โœจ All Python files were already formatted!" + fi +fi + diff --git a/scripts/lint/all.sh b/scripts/lint/all.sh new file mode 100755 index 0000000..0624493 --- /dev/null +++ b/scripts/lint/all.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -e + +echo "๐Ÿ” Linting all files (C++/CUDA + Python)..." +echo "" + +# Lint C++/CUDA files +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +if [ -f "scripts/lint/cpp.sh" ]; then + scripts/lint/cpp.sh + CPP_EXIT=$? +else + echo "โš ๏ธ C++/CUDA lint script not found" + CPP_EXIT=0 +fi + +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + +# Lint Python files +if [ -f "scripts/lint/python.sh" ]; then + scripts/lint/python.sh + PYTHON_EXIT=$? +else + echo "โš ๏ธ Python lint script not found" + PYTHON_EXIT=0 +fi + +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + +if [ $CPP_EXIT -eq 0 ] && [ $PYTHON_EXIT -eq 0 ]; then + echo "โœจ All files passed linting!" + exit 0 +else + echo "โŒ Found linting issues." + exit 1 +fi + diff --git a/scripts/lint/cpp-fix.sh b/scripts/lint/cpp-fix.sh new file mode 100755 index 0000000..1364657 --- /dev/null +++ b/scripts/lint/cpp-fix.sh @@ -0,0 +1,48 @@ +#!/bin/bash +set -e + +FILES=$(find genmetaballs/src/cuda tests -type f \( -name '*.cu' -o -name '*.cuh' \) 2>/dev/null || true) + +if [ -z "$FILES" ]; then + echo "โœ“ No CUDA/C++ files found" + exit 0 +fi + +USE_COMPILE_COMMANDS=false +if [ "$1" = "full" ]; then + USE_COMPILE_COMMANDS=true + if [ ! -f "build/compile_commands.json" ]; then + echo "โš ๏ธ compile_commands.json not found. Running 'pixi run compile-commands' first..." + cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON >/dev/null 2>&1 || true + if [ ! -f "build/compile_commands.json" ]; then + echo "โŒ Failed to generate compile_commands.json" + exit 1 + fi + fi +fi + +echo "๐Ÿ”ง Auto-fixing linting issues in CUDA/C++ files..." +FIXED=0 +HAS_ISSUES=0 + +while IFS= read -r file; do + if [ -f "$file" ]; then + if [ "$USE_COMPILE_COMMANDS" = true ]; then + # Use compile_commands.json for better analysis + OUTPUT=$(clang-tidy --fix "$file" 2>&1 || true) + else + # Use manual flags + OUTPUT=$(clang-tidy --fix "$file" -- -Igenmetaballs/src/cuda -std=c++20 2>&1 || true) + fi + + # Check if any fixes were applied (clang-tidy modifies files in place) + # We can't easily detect what was fixed, so we'll just report the file was processed + echo " โœ“ Processed: $file" + FIXED=1 + fi +done <<< "$FILES" + +echo "" +echo "โœจ Auto-fix complete! ($(echo "$FILES" | wc -l) files processed)" +echo "๐Ÿ’ก Note: Review the changes and run 'pixi run lint' to check remaining issues" + diff --git a/scripts/lint/cpp.sh b/scripts/lint/cpp.sh new file mode 100755 index 0000000..1336dae --- /dev/null +++ b/scripts/lint/cpp.sh @@ -0,0 +1,122 @@ +#!/bin/bash +set -e + +FILES=$(find genmetaballs/src/cuda tests -type f \( -name '*.cu' -o -name '*.cuh' \) 2>/dev/null || true) + +if [ -z "$FILES" ]; then + echo "โœ“ No CUDA/C++ files found" + exit 0 +fi + +USE_COMPILE_COMMANDS=true +if [ "$1" = "basic" ]; then + USE_COMPILE_COMMANDS=false +fi + +# Try to use compile_commands.json if available (faster and more accurate) +if [ "$USE_COMPILE_COMMANDS" = true ]; then + if [ ! -f "build/compile_commands.json" ]; then + echo "โš ๏ธ compile_commands.json not found. Generating it..." + cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON >/dev/null 2>&1 || true + if [ ! -f "build/compile_commands.json" ]; then + echo "โš ๏ธ Could not generate compile_commands.json, falling back to basic mode" + USE_COMPILE_COMMANDS=false + fi + fi +fi + +echo "๐Ÿ” Linting CUDA/C++ files..." +HAS_ISSUES=0 +CLEAN_FILES=0 +FILES_WITH_ISSUES=0 + +# Create temporary directory for parallel processing results +TMPDIR=$(mktemp -d) +trap "rm -rf $TMPDIR" EXIT + +# Export variables for parallel execution +export USE_COMPILE_COMMANDS +export TMPDIR + +# Run clang-tidy in parallel (use number of CPU cores) +NPROC=$(nproc 2>/dev/null || echo 4) +echo "$FILES" | xargs -P "$NPROC" -I {} sh -c ' + file="$1" + use_compile_commands="$2" + tmpdir="$3" + + if [ ! -f "$file" ]; then + exit 0 + fi + + if [ "$use_compile_commands" = "true" ]; then + OUTPUT=$(clang-tidy "$file" 2>&1 || true) + else + OUTPUT=$(clang-tidy "$file" -- -Igenmetaballs/src/cuda -std=c++20 2>&1 || true) + fi + + FILE_BASENAME=$(basename "$file") + ISSUES=$(echo "$OUTPUT" | grep -E "(^|/)$FILE_BASENAME:" | \ + grep -E ": (warning|error):" | \ + grep -v "suppressed" | \ + grep -v "clang-diagnostic-error" | \ + grep -v "__clang_cuda_runtime_wrapper.h" | \ + grep -v "CUDA version is newer" | \ + grep -v "unable to handle compilation" | \ + grep -v "unused-command-line-argument" | \ + grep -v "linker.*input unused" | \ + grep -v "Error parsing.*clang-tidy" | \ + grep -v "Error while processing" | \ + grep -v "Found compiler error" || true) + + tmpfile="$tmpdir/$(basename "$file" | tr "/" "_").lint" + if [ -z "$ISSUES" ]; then + echo "CLEAN:$file" > "$tmpfile" + else + echo "ISSUES:$file" > "$tmpfile" + echo "$ISSUES" >> "$tmpfile" + fi +' _ {} "$USE_COMPILE_COMMANDS" "$TMPDIR" + +# Process results in order +OLD_IFS="$IFS" +IFS=$'\n' +for file in $FILES; do + IFS="$OLD_IFS" + if [ -f "$file" ]; then + tmpfile="$TMPDIR/$(basename "$file" | tr "/" "_").lint" + if [ -f "$tmpfile" ]; then + FIRST_LINE=$(head -n 1 "$tmpfile") + if echo "$FIRST_LINE" | grep -q "^CLEAN:"; then + echo " โœ“ $file" + CLEAN_FILES=$((CLEAN_FILES + 1)) + elif echo "$FIRST_LINE" | grep -q "^ISSUES:"; then + echo " โœ— $file" + # Show issues with proper indentation + tail -n +2 "$tmpfile" | while IFS= read -r line; do + if echo "$line" | grep -qE ":[0-9]+:[0-9]+: (warning|error):"; then + echo "$line" | sed -E 's/^[^:]+:([0-9]+:[0-9]+: (warning|error):.*)/ \1/' + else + echo " $line" + fi + done + FILES_WITH_ISSUES=$((FILES_WITH_ISSUES + 1)) + HAS_ISSUES=1 + fi + fi + fi +done +IFS="$OLD_IFS" + +echo "" +if [ $HAS_ISSUES -eq 0 ]; then + echo "โœจ All files passed linting! ($CLEAN_FILES files checked)" + exit 0 +else + echo "โŒ Found issues in $FILES_WITH_ISSUES file(s). ($CLEAN_FILES files clean)" + if [ "$USE_COMPILE_COMMANDS" = false ]; then + echo "๐Ÿ’ก Tip: Run 'pixi run lint-full' for more accurate analysis using compile_commands.json" + fi + exit 1 +fi + diff --git a/scripts/lint/fix-all.sh b/scripts/lint/fix-all.sh new file mode 100755 index 0000000..ca73da0 --- /dev/null +++ b/scripts/lint/fix-all.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set -e + +echo "๐Ÿ”ง Auto-fixing linting issues in all files (C++/CUDA + Python)..." +echo "" + +# Fix C++/CUDA files +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +if [ -f "scripts/lint/cpp-fix.sh" ]; then + scripts/lint/cpp-fix.sh + CPP_EXIT=$? +else + echo "โš ๏ธ C++/CUDA lint-fix script not found" + CPP_EXIT=0 +fi + +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + +# Fix Python files +if [ -f "scripts/lint/python-fix.sh" ]; then + scripts/lint/python-fix.sh + PYTHON_EXIT=$? +else + echo "โš ๏ธ Python lint-fix script not found" + PYTHON_EXIT=0 +fi + +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +echo "โœจ Auto-fix complete!" + +if [ $CPP_EXIT -ne 0 ] || [ $PYTHON_EXIT -ne 0 ]; then + exit 1 +fi + diff --git a/scripts/lint/python-fix.sh b/scripts/lint/python-fix.sh new file mode 100755 index 0000000..3872859 --- /dev/null +++ b/scripts/lint/python-fix.sh @@ -0,0 +1,42 @@ +#!/bin/bash +set -e + +PYTHON_FILES=$(find genmetaballs tests -type f -name '*.py' 2>/dev/null | sort || true) + +if [ -z "$PYTHON_FILES" ]; then + echo "โœ“ No Python files found" + exit 0 +fi + +echo "๐Ÿ”ง Auto-fixing linting issues in Python files..." +FIXED_COUNT=0 +ISSUE_COUNT=0 + +# Run ruff check with --fix +RUFF_OUTPUT=$(ruff check --fix $PYTHON_FILES 2>&1 || true) +RUFF_EXIT=$? + +# Process each file to show what was fixed +while IFS= read -r file; do + if [ -f "$file" ]; then + # Check if this file had any issues + FILE_ISSUES=$(echo "$RUFF_OUTPUT" | grep "^$file" || true) + if [ -z "$FILE_ISSUES" ]; then + echo " โœ“ $file (no issues)" + else + echo " โœ— $file (fixed issues)" + echo "$FILE_ISSUES" | sed 's/^/ /' + ISSUE_COUNT=$((ISSUE_COUNT + 1)) + fi + FIXED_COUNT=$((FIXED_COUNT + 1)) + fi +done <<< "$PYTHON_FILES" + +echo "" +if [ $RUFF_EXIT -eq 0 ] && [ $ISSUE_COUNT -eq 0 ]; then + echo "โœจ All files are clean! ($FIXED_COUNT files checked)" +else + echo "โœจ Auto-fix complete! ($ISSUE_COUNT files had fixes applied)" + echo "๐Ÿ’ก Note: Some issues may require manual fixes. Run 'pixi run lint-python' to check remaining issues" +fi + diff --git a/scripts/lint/python.sh b/scripts/lint/python.sh new file mode 100755 index 0000000..56f35e8 --- /dev/null +++ b/scripts/lint/python.sh @@ -0,0 +1,110 @@ +#!/bin/bash +set -e + +PYTHON_FILES=$(find genmetaballs tests -type f -name '*.py' 2>/dev/null | sort || true) + +if [ -z "$PYTHON_FILES" ]; then + echo "โœ“ No Python files found" + exit 0 +fi + +echo "๐Ÿ” Linting Python files..." +HAS_ISSUES=0 +CLEAN_COUNT=0 +ISSUE_COUNT=0 + +# Run ruff check and capture output +set +e +RUFF_OUTPUT=$(ruff check $PYTHON_FILES 2>&1) +RUFF_EXIT=$? +set -e + +# If ruff found issues, show them grouped by file +if [ $RUFF_EXIT -ne 0 ] && [ -n "$RUFF_OUTPUT" ]; then + # Process each file + OLD_IFS="$IFS" + IFS=$'\n' + FILES_WITH_ISSUES="" + for file in $PYTHON_FILES; do + IFS="$OLD_IFS" + if [ -f "$file" ]; then + # Check if this file appears in the error output + if echo "$RUFF_OUTPUT" | grep -q "$file"; then + echo " โœ— $file" + # Extract error blocks for this file - from error code through help line + echo "$RUFF_OUTPUT" | python3 -c " +import sys +file = '$file' +lines = sys.stdin.readlines() +in_block = False +block_lines = [] +for i, line in enumerate(lines): + # Start of error block for our file + if file in line and '-->' in line: + # Print previous block if any + if block_lines: + for bl in block_lines: + print(' ' + bl.rstrip()) + block_lines = [] + in_block = True + # Find the error code line before this (look back up to 2 lines) + for j in range(max(0, i-2), i): + if j < len(lines) and lines[j].strip(): + stripped = lines[j].strip() + if len(stripped) >= 2 and stripped[0].isupper() and stripped[1].isdigit(): + block_lines.append(lines[j]) + break + block_lines.append(line) + elif in_block: + block_lines.append(line) + # End of block at help: line or next error code or Found + if 'help:' in line or (line.strip() and line[0].isupper() and line[1].isdigit() and file not in line): + in_block = False + for bl in block_lines: + print(' ' + bl.rstrip()) + block_lines = [] + elif 'Found' in line: + in_block = False + if block_lines: + for bl in block_lines: + print(' ' + bl.rstrip()) + block_lines = [] +if block_lines: + for bl in block_lines: + print(' ' + bl.rstrip()) +" + FILES_WITH_ISSUES="$FILES_WITH_ISSUES $file" + ISSUE_COUNT=$((ISSUE_COUNT + 1)) + HAS_ISSUES=1 + else + echo " โœ“ $file" + CLEAN_COUNT=$((CLEAN_COUNT + 1)) + fi + fi + done + IFS="$OLD_IFS" +else + # All files are clean + OLD_IFS="$IFS" + IFS=$'\n' + for file in $PYTHON_FILES; do + IFS="$OLD_IFS" + if [ -f "$file" ]; then + echo " โœ“ $file" + CLEAN_COUNT=$((CLEAN_COUNT + 1)) + fi + done + IFS="$OLD_IFS" +fi + +echo "" +if [ $RUFF_EXIT -eq 0 ] && [ $HAS_ISSUES -eq 0 ]; then + TOTAL_FILES=$(echo "$PYTHON_FILES" | wc -l) + echo "โœจ All Python files passed linting! ($TOTAL_FILES files checked)" + exit 0 +else + echo "โŒ Found issues in $ISSUE_COUNT file(s). ($CLEAN_COUNT files clean)" + echo "๐Ÿ’ก Tip: Run 'ruff check --fix' to auto-fix some issues" + exit 1 +fi + diff --git a/tests/test_add.cu b/tests/test_add.cu index 42964e4..86dddcf 100644 --- a/tests/test_add.cu +++ b/tests/test_add.cu @@ -1,18 +1,17 @@ #include #include +#include #include -#include #include "core/add.cuh" -TEST(GpuAddTest, BasicAddition) -{ +TEST(GpuAddTest, BasicAddition) { constexpr uint32_t N = 4096; constexpr uint32_t block_dim = 1024; constexpr uint32_t grid_dim = 4; std::vector a_vec(N), b_vec(N), cpu_sum_vec(N); - for(uint32_t i = 0; i < N; i++) { + for (uint32_t i = 0; i < N; i++) { a_vec[i] = 2 * i * 3.14; b_vec[i] = (2 * i + 1) * 2.71; cpu_sum_vec[i] = a_vec[i] + b_vec[i]; @@ -20,8 +19,7 @@ TEST(GpuAddTest, BasicAddition) auto gpu_sum_vec = gpu_add(a_vec, b_vec); - for(uint32_t i = 0; i < N; i++) { - EXPECT_FLOAT_EQ(cpu_sum_vec[i], gpu_sum_vec[i]) - << "Mismatch at index " << i; + for (uint32_t i = 0; i < N; i++) { + EXPECT_FLOAT_EQ(cpu_sum_vec[i], gpu_sum_vec[i]) << "Mismatch at index " << i; } } diff --git a/tests/test_gpu_add.py b/tests/test_gpu_add.py index 2ab77a0..9b61f9f 100644 --- a/tests/test_gpu_add.py +++ b/tests/test_gpu_add.py @@ -15,4 +15,4 @@ def test_gpu_add(rng: np.random.Generator) -> None: a = rng.normal(size=N).astype(np.float32).tolist() b = rng.normal(size=N).astype(np.float32).tolist() c = gpu_add(a, b) - assert all(abs(x + y - z) < 1e-6 for (x, y, z) in zip(a, b, c)) + assert all(abs(x + y - z) < 1e-6 for (x, y, z) in zip(a, b, c, strict=True)) From 0d4acab1d838b9ec2c1c41376da1e21690cba0c4 Mon Sep 17 00:00:00 2001 From: Arijit Dasgupta Date: Sun, 9 Nov 2025 07:35:35 +0000 Subject: [PATCH 02/12] added pre-commit and pre-push hooks --- .pre-commit-config.yaml | 43 ++++++++++++++++ README.md | 26 ++++++++++ pixi.lock | 99 +++++++++++++++++++++++++++++++++++- pyproject.toml | 8 +++ scripts/git-hooks/pre-commit | 57 +++++++++++++++++++++ scripts/git-hooks/pre-push | 28 ++++++++++ scripts/install-git-hooks.sh | 42 +++++++++++++++ scripts/test-quiet.sh | 35 +++++++++++++ 8 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 .pre-commit-config.yaml create mode 100755 scripts/git-hooks/pre-commit create mode 100755 scripts/git-hooks/pre-push create mode 100755 scripts/install-git-hooks.sh create mode 100755 scripts/test-quiet.sh diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..01988c8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,43 @@ +# Pre-commit hooks configuration for GenMetaBalls +# See https://pre-commit.com for more information + +repos: + # Local hooks for custom formatting and linting + - repo: local + hooks: + # Format files (C++/CUDA + Python) + - id: format + name: Format files + entry: pixi run format + language: system + pass_filenames: false + always_run: true + stages: [pre-commit] + + # Check formatting (C++/CUDA + Python) + - id: format-check + name: Check formatting + entry: pixi run format-check + language: system + pass_filenames: false + always_run: true + stages: [pre-commit] + + # Lint files (C++/CUDA + Python) + - id: lint + name: Lint files + entry: pixi run lint + language: system + pass_filenames: false + always_run: true + stages: [pre-commit] + + # Run tests (pre-push hook) + - id: test + name: Run all tests + entry: scripts/test-quiet.sh + language: system + pass_filenames: false + always_run: true + stages: [pre-push] + diff --git a/README.md b/README.md index 4b903eb..916658f 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,32 @@ For initial installation, run: pixi install ``` +### Development Setup + +To install and use GenMetaBalls: + +```bash +pixi install +``` + +For development (formatting, linting, git hooks, testing): + +```bash +pixi install +pixi run dev-setup +``` + +This sets up [pre-commit](https://pre-commit.com/) git hooks: +- **Pre-commit**: Formats and lints code before each commit +- **Pre-push**: Runs all tests before pushes + +Run the hooks manually if needed: +```bash +pixi run pre-commit-run # Run formatting/linting checks +pixi run pre-commit-run-push # Run all tests +``` +``` + ## Testing ### C++/CUDA Tests diff --git a/pixi.lock b/pixi.lock index 5369d14..4e4ae37 100644 --- a/pixi.lock +++ b/pixi.lock @@ -209,12 +209,21 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda + - pypi: https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e8/7e/4a14a769741fbf237eec5a12a2cbc7a4c4e061852b6533bcb9e9a796c908/numpy-2.3.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/27/11/574fe7d13acf30bfd0a8dd7fa1647040f2b8064f13f43e8c963b1e65093b/pre_commit-4.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/ee/79/6ad4dda2cfd55e41ac9ed6d73ef9ab9475b1eef69f3a85957210c74ba12c/ruff-0.14.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl - pypi: ./ packages: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 @@ -335,6 +344,11 @@ packages: purls: [] size: 155907 timestamp: 1759649036195 +- pypi: https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl + name: cfgv + version: 3.4.0 + sha256: b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9 + requires_python: '>=3.8' - conda: https://conda.anaconda.org/conda-forge/linux-64/clang-format-19.1.7-default_h99862b1_5.conda sha256: 118af30168d24a08c6e6443c269bcef5369262003e7dda227decea14b73534b4 md5: 572bc6b9b35539a2e152eb25987c1558 @@ -1014,6 +1028,10 @@ packages: purls: [] size: 437860 timestamp: 1747855126005 +- pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl + name: distlib + version: 0.4.0 + sha256: 9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16 - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda sha256: ce61f4f99401a4bd455b89909153b40b9c823276aefcbb06f2044618696009ca md5: 72e42d28960d875c7654614f8b50939a @@ -1025,6 +1043,11 @@ packages: - pkg:pypi/exceptiongroup?source=hash-mapping size: 21284 timestamp: 1746947398083 +- pypi: https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl + name: filelock + version: 3.20.0 + sha256: 339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2 + requires_python: '>=3.10' - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 sha256: 58d7f40d2940dd0a8aa28651239adbf5613254df0f75789919c4e6762054403b md5: 0c96522c6bdaed4b1566d11387caaf45 @@ -1161,7 +1184,7 @@ packages: - pypi: ./ name: genmetaballs version: 0.0.1 - sha256: 4640ace5bf014126fce6f3f949d3d4e312cb04fa371fe80b6f95274e7eb84ee6 + sha256: 26221218d66c402421decc9e58527a501efb48874693ee046ce950bb972b500d requires_dist: - numpy requires_python: '>=3.13' @@ -1239,6 +1262,13 @@ packages: purls: [] size: 12129203 timestamp: 1720853576813 +- pypi: https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl + name: identify + version: 2.6.15 + sha256: 1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757 + requires_dist: + - ukkonen ; extra == 'license' + requires_python: '>=3.9' - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda sha256: c18ab120a0613ada4391b15981d86ff777b5690ca461ea7e9e49531e8f374745 md5: 63ccfdc3a3ce25b027b8767eb722fca8 @@ -2200,6 +2230,11 @@ packages: purls: [] size: 186326 timestamp: 1752218296032 +- pypi: https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl + name: nodeenv + version: 1.9.1 + sha256: ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9 + requires_python: '>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*' - conda: https://conda.anaconda.org/conda-forge/linux-64/nsight-compute-2025.1.1.2-hb5ebaad_1.conda sha256: 6e887bc56ee7a658f1ffefa9877b4adcc9f51b8894e1cd13c6de8b304809a7a7 md5: 9d7247e32f652f7764e1579c93a86b2a @@ -2353,6 +2388,22 @@ packages: purls: [] size: 1209177 timestamp: 1756742976157 +- pypi: https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl + name: platformdirs + version: 4.5.0 + sha256: e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3 + requires_dist: + - furo>=2025.9.25 ; extra == 'docs' + - proselint>=0.14 ; extra == 'docs' + - sphinx-autodoc-typehints>=3.2 ; extra == 'docs' + - sphinx>=8.2.3 ; extra == 'docs' + - appdirs==1.4.4 ; extra == 'test' + - covdefaults>=2.3 ; extra == 'test' + - pytest-cov>=7 ; extra == 'test' + - pytest-mock>=3.15.1 ; extra == 'test' + - pytest>=8.4.2 ; extra == 'test' + - mypy>=1.18.2 ; extra == 'type' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl name: pluggy version: 1.6.0 @@ -2364,6 +2415,17 @@ packages: - pytest-benchmark ; extra == 'testing' - coverage ; extra == 'testing' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/27/11/574fe7d13acf30bfd0a8dd7fa1647040f2b8064f13f43e8c963b1e65093b/pre_commit-4.4.0-py2.py3-none-any.whl + name: pre-commit + version: 4.4.0 + sha256: b35ea52957cbf83dcc5d8ee636cbead8624e3a15fbfa61a370e42158ac8a5813 + requires_dist: + - cfgv>=2.0.0 + - identify>=1.0.0 + - nodeenv>=0.11.1 + - pyyaml>=5.1 + - virtualenv>=20.10.0 + requires_python: '>=3.10' - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda sha256: 9c88f8c64590e9567c6c80823f0328e58d3b1efb0e1c539c0315ceca764e0973 md5: b3c17d95b5a10c6e64a21fa17573e70e @@ -2441,6 +2503,11 @@ packages: purls: [] size: 6989 timestamp: 1752805904792 +- pypi: https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: pyyaml + version: 6.0.3 + sha256: c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 + requires_python: '>=3.8' - conda: https://conda.anaconda.org/conda-forge/linux-64/rdma-core-60.0-hecca717_0.conda sha256: 5c09b833b698ecd19da14f5ff903063cf174382d6b32c86166984a93d427d681 md5: fe7412835a65cd99eacf3afbb124c7ac @@ -2568,6 +2635,36 @@ packages: purls: [] size: 122968 timestamp: 1742727099393 +- pypi: https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl + name: virtualenv + version: 20.35.4 + sha256: c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b + requires_dist: + - distlib>=0.3.7,<1 + - filelock>=3.12.2,<4 + - importlib-metadata>=6.6 ; python_full_version < '3.8' + - platformdirs>=3.9.1,<5 + - typing-extensions>=4.13.2 ; python_full_version < '3.11' + - furo>=2023.7.26 ; extra == 'docs' + - proselint>=0.13 ; extra == 'docs' + - sphinx>=7.1.2,!=7.3 ; extra == 'docs' + - sphinx-argparse>=0.4 ; extra == 'docs' + - sphinxcontrib-towncrier>=0.2.1a0 ; extra == 'docs' + - towncrier>=23.6 ; extra == 'docs' + - covdefaults>=2.3 ; extra == 'test' + - coverage-enable-subprocess>=1 ; extra == 'test' + - coverage>=7.2.7 ; extra == 'test' + - flaky>=3.7 ; extra == 'test' + - packaging>=23.1 ; extra == 'test' + - pytest-env>=0.8.2 ; extra == 'test' + - pytest-freezer>=0.4.8 ; (python_full_version >= '3.13' and platform_python_implementation == 'CPython' and sys_platform == 'win32' and extra == 'test') or (platform_python_implementation == 'GraalVM' and extra == 'test') or (platform_python_implementation == 'PyPy' and extra == 'test') + - pytest-mock>=3.11.1 ; extra == 'test' + - pytest-randomly>=3.12 ; extra == 'test' + - pytest-timeout>=2.1 ; extra == 'test' + - pytest>=7.4 ; extra == 'test' + - setuptools>=68 ; extra == 'test' + - time-machine>=2.10 ; platform_python_implementation == 'CPython' and extra == 'test' + requires_python: '>=3.8' - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-hd6090a7_1.conda sha256: 3aa04ae8e9521d9b56b562376d944c3e52b69f9d2a0667f77b8953464822e125 md5: 035da2e4f5770f036ff704fa17aace24 diff --git a/pyproject.toml b/pyproject.toml index e48f6d3..d44cf8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ clang-tools = ">=18,<20" # Includes clang-format and clang-tidy [tool.pixi.pypi-dependencies] genmetaballs = { path = ".", editable = true } ruff = ">=0.8.0" +pre-commit = ">=3.0.0" [tool.pixi.tasks] ctest = "ctest --test-dir build" @@ -59,6 +60,13 @@ format-python = { cmd = "scripts/format/python.sh" } format-check-python = { cmd = "scripts/format/python.sh check" } lint-python = { cmd = "scripts/lint/python.sh" } lint-python-fix = { cmd = "scripts/lint/python-fix.sh" } +# Git hooks (using pre-commit framework) +install-hooks = { cmd = "pre-commit install && pre-commit install --hook-type pre-push" } +# Dev setup: install git hooks (for development workflow) +dev-setup = { cmd = "pre-commit install && pre-commit install --hook-type pre-push" } +# Pre-commit tasks +pre-commit-run = { cmd = "pre-commit run --all-files" } +pre-commit-run-push = { cmd = "scripts/test-quiet.sh" } [tool.pixi.environments] default = { solve-group = "default", features = ["dev"] } diff --git a/scripts/git-hooks/pre-commit b/scripts/git-hooks/pre-commit new file mode 100755 index 0000000..666bd70 --- /dev/null +++ b/scripts/git-hooks/pre-commit @@ -0,0 +1,57 @@ +#!/bin/bash +# Pre-commit hook for GenMetaBalls +# Runs formatting and linting checks before allowing commits + +set -e + +echo "๐Ÿ”ง Running pre-commit checks..." +echo "" + +# Step 1: Format files +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +echo "Step 1/3: Formatting files..." +FORMAT_OUTPUT=$(pixi run format 2>&1) +FORMAT_EXIT=$? +if [ $FORMAT_EXIT -ne 0 ]; then + echo "$FORMAT_OUTPUT" + echo "" + echo "โŒ Formatting failed. Please fix the errors above." + exit 1 +fi +echo "โœ“ Formatting complete" +echo "" + +# Step 2: Check formatting +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +echo "Step 2/3: Checking formatting..." +FORMAT_CHECK_OUTPUT=$(pixi run format-check 2>&1) +FORMAT_CHECK_EXIT=$? +if [ $FORMAT_CHECK_EXIT -ne 0 ]; then + echo "$FORMAT_CHECK_OUTPUT" + echo "" + echo "โŒ Format check failed. Some files still need formatting." + echo "๐Ÿ’ก Run 'pixi run format' to fix formatting issues." + exit 1 +fi +echo "โœ“ Format check passed" +echo "" + +# Step 3: Lint files +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +echo "Step 3/3: Linting files..." +LINT_OUTPUT=$(pixi run lint 2>&1) +LINT_EXIT=$? +if [ $LINT_EXIT -ne 0 ]; then + echo "$LINT_OUTPUT" + echo "" + echo "โŒ Linting failed. Please fix the linting errors above." + echo "๐Ÿ’ก Run 'pixi run lint-fix' to auto-fix some issues." + exit 1 +fi +echo "โœ“ Linting passed" +echo "" + +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +echo "โœจ All pre-commit checks passed!" +exit 0 + diff --git a/scripts/git-hooks/pre-push b/scripts/git-hooks/pre-push new file mode 100755 index 0000000..a9816db --- /dev/null +++ b/scripts/git-hooks/pre-push @@ -0,0 +1,28 @@ +#!/bin/bash +# Pre-push hook for GenMetaBalls +# Runs all tests before allowing push + +set -e + +echo "๐Ÿงช Running pre-push tests..." + +# Run all tests (C++/CUDA + Python) +TEST_OUTPUT=$(pixi run test 2>&1) +TEST_EXIT=$? + +if [ $TEST_EXIT -ne 0 ]; then + echo "" + echo "$TEST_OUTPUT" + echo "" + echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + echo "โŒ Tests failed. Push blocked." + echo "๐Ÿ’ก Fix the failing tests before pushing." + exit 1 +fi + +# Extract summary lines for success case (less verbose) +echo "$TEST_OUTPUT" | grep -E "(tests passed|passed in|Test #)" | head -5 +echo "" +echo "โœจ All tests passed! Push allowed." +exit 0 + diff --git a/scripts/install-git-hooks.sh b/scripts/install-git-hooks.sh new file mode 100755 index 0000000..19f6423 --- /dev/null +++ b/scripts/install-git-hooks.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# Install git hooks for GenMetaBalls + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +HOOKS_DIR="$REPO_ROOT/.git/hooks" +GIT_HOOKS_SOURCE="$REPO_ROOT/scripts/git-hooks" + +if [ ! -d "$HOOKS_DIR" ]; then + echo "โŒ .git/hooks directory not found. Are you in a git repository?" + exit 1 +fi + +echo "๐Ÿ“ฆ Installing git hooks..." + +# Install pre-commit hook +if [ -f "$GIT_HOOKS_SOURCE/pre-commit" ]; then + cp "$GIT_HOOKS_SOURCE/pre-commit" "$HOOKS_DIR/pre-commit" + chmod +x "$HOOKS_DIR/pre-commit" + echo "โœ“ Installed pre-commit hook" +else + echo "โš ๏ธ pre-commit hook not found at $GIT_HOOKS_SOURCE/pre-commit" + exit 1 +fi + +# Install pre-push hook +if [ -f "$GIT_HOOKS_SOURCE/pre-push" ]; then + cp "$GIT_HOOKS_SOURCE/pre-push" "$HOOKS_DIR/pre-push" + chmod +x "$HOOKS_DIR/pre-push" + echo "โœ“ Installed pre-push hook" +else + echo "โš ๏ธ pre-push hook not found at $GIT_HOOKS_SOURCE/pre-push" + exit 1 +fi + +echo "" +echo "โœจ Git hooks installed successfully!" +echo "๐Ÿ’ก Pre-commit hook: Runs formatting and linting checks before each commit" +echo "๐Ÿ’ก Pre-push hook: Runs all tests before allowing push" + diff --git a/scripts/test-quiet.sh b/scripts/test-quiet.sh new file mode 100755 index 0000000..0156092 --- /dev/null +++ b/scripts/test-quiet.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# Run tests with minimal output on success, full output on failure + +set -e + +# Run ctest (not using --quiet to get summary) +CTEST_OUTPUT=$(pixi run ctest --test-dir build 2>&1) +CTEST_EXIT=$? + +if [ $CTEST_EXIT -ne 0 ]; then + echo "$CTEST_OUTPUT" + exit 1 +fi + +# Extract summary from ctest output +echo "C++/CUDA tests:" +echo "$CTEST_OUTPUT" | grep -E "(tests passed|Test #.*Passed)" | head -2 | sed 's/^/ /' + +# Run pytest with quiet flag +PYTEST_OUTPUT=$(pixi run pytest --quiet 2>&1) +PYTEST_EXIT=$? + +if [ $PYTEST_EXIT -ne 0 ]; then + echo "" + echo "Python tests:" + echo "$PYTEST_OUTPUT" + exit 1 +fi + +# Extract summary from pytest output (quiet mode shows minimal output) +echo "Python tests:" +echo "$PYTEST_OUTPUT" | grep -E "(passed|failed)" | tail -1 | sed 's/^/ /' + +exit 0 + From 0bc8555dbdc5b5345b06f6f4ac2dd19c455c3eec Mon Sep 17 00:00:00 2001 From: Arijit Dasgupta Date: Sun, 9 Nov 2025 07:37:04 +0000 Subject: [PATCH 03/12] test From 2de2cd46ccaeba82870dd1f878df5a4b1d9bc48d Mon Sep 17 00:00:00 2001 From: Arijit Dasgupta Date: Sun, 9 Nov 2025 07:38:29 +0000 Subject: [PATCH 04/12] edit pre-commit files --- scripts/git-hooks/pre-commit | 57 ------------------------------------ scripts/git-hooks/pre-push | 28 ------------------ scripts/install-git-hooks.sh | 42 -------------------------- 3 files changed, 127 deletions(-) delete mode 100755 scripts/git-hooks/pre-commit delete mode 100755 scripts/git-hooks/pre-push delete mode 100755 scripts/install-git-hooks.sh diff --git a/scripts/git-hooks/pre-commit b/scripts/git-hooks/pre-commit deleted file mode 100755 index 666bd70..0000000 --- a/scripts/git-hooks/pre-commit +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash -# Pre-commit hook for GenMetaBalls -# Runs formatting and linting checks before allowing commits - -set -e - -echo "๐Ÿ”ง Running pre-commit checks..." -echo "" - -# Step 1: Format files -echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" -echo "Step 1/3: Formatting files..." -FORMAT_OUTPUT=$(pixi run format 2>&1) -FORMAT_EXIT=$? -if [ $FORMAT_EXIT -ne 0 ]; then - echo "$FORMAT_OUTPUT" - echo "" - echo "โŒ Formatting failed. Please fix the errors above." - exit 1 -fi -echo "โœ“ Formatting complete" -echo "" - -# Step 2: Check formatting -echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" -echo "Step 2/3: Checking formatting..." -FORMAT_CHECK_OUTPUT=$(pixi run format-check 2>&1) -FORMAT_CHECK_EXIT=$? -if [ $FORMAT_CHECK_EXIT -ne 0 ]; then - echo "$FORMAT_CHECK_OUTPUT" - echo "" - echo "โŒ Format check failed. Some files still need formatting." - echo "๐Ÿ’ก Run 'pixi run format' to fix formatting issues." - exit 1 -fi -echo "โœ“ Format check passed" -echo "" - -# Step 3: Lint files -echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" -echo "Step 3/3: Linting files..." -LINT_OUTPUT=$(pixi run lint 2>&1) -LINT_EXIT=$? -if [ $LINT_EXIT -ne 0 ]; then - echo "$LINT_OUTPUT" - echo "" - echo "โŒ Linting failed. Please fix the linting errors above." - echo "๐Ÿ’ก Run 'pixi run lint-fix' to auto-fix some issues." - exit 1 -fi -echo "โœ“ Linting passed" -echo "" - -echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" -echo "โœจ All pre-commit checks passed!" -exit 0 - diff --git a/scripts/git-hooks/pre-push b/scripts/git-hooks/pre-push deleted file mode 100755 index a9816db..0000000 --- a/scripts/git-hooks/pre-push +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -# Pre-push hook for GenMetaBalls -# Runs all tests before allowing push - -set -e - -echo "๐Ÿงช Running pre-push tests..." - -# Run all tests (C++/CUDA + Python) -TEST_OUTPUT=$(pixi run test 2>&1) -TEST_EXIT=$? - -if [ $TEST_EXIT -ne 0 ]; then - echo "" - echo "$TEST_OUTPUT" - echo "" - echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" - echo "โŒ Tests failed. Push blocked." - echo "๐Ÿ’ก Fix the failing tests before pushing." - exit 1 -fi - -# Extract summary lines for success case (less verbose) -echo "$TEST_OUTPUT" | grep -E "(tests passed|passed in|Test #)" | head -5 -echo "" -echo "โœจ All tests passed! Push allowed." -exit 0 - diff --git a/scripts/install-git-hooks.sh b/scripts/install-git-hooks.sh deleted file mode 100755 index 19f6423..0000000 --- a/scripts/install-git-hooks.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -# Install git hooks for GenMetaBalls - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" -HOOKS_DIR="$REPO_ROOT/.git/hooks" -GIT_HOOKS_SOURCE="$REPO_ROOT/scripts/git-hooks" - -if [ ! -d "$HOOKS_DIR" ]; then - echo "โŒ .git/hooks directory not found. Are you in a git repository?" - exit 1 -fi - -echo "๐Ÿ“ฆ Installing git hooks..." - -# Install pre-commit hook -if [ -f "$GIT_HOOKS_SOURCE/pre-commit" ]; then - cp "$GIT_HOOKS_SOURCE/pre-commit" "$HOOKS_DIR/pre-commit" - chmod +x "$HOOKS_DIR/pre-commit" - echo "โœ“ Installed pre-commit hook" -else - echo "โš ๏ธ pre-commit hook not found at $GIT_HOOKS_SOURCE/pre-commit" - exit 1 -fi - -# Install pre-push hook -if [ -f "$GIT_HOOKS_SOURCE/pre-push" ]; then - cp "$GIT_HOOKS_SOURCE/pre-push" "$HOOKS_DIR/pre-push" - chmod +x "$HOOKS_DIR/pre-push" - echo "โœ“ Installed pre-push hook" -else - echo "โš ๏ธ pre-push hook not found at $GIT_HOOKS_SOURCE/pre-push" - exit 1 -fi - -echo "" -echo "โœจ Git hooks installed successfully!" -echo "๐Ÿ’ก Pre-commit hook: Runs formatting and linting checks before each commit" -echo "๐Ÿ’ก Pre-push hook: Runs all tests before allowing push" - From 8f003185e912d979c5c3740a91652f10d1b39eba Mon Sep 17 00:00:00 2001 From: Arijit Dasgupta Date: Sun, 9 Nov 2025 07:41:17 +0000 Subject: [PATCH 05/12] test verbose output --- .pre-commit-config.yaml | 1 + scripts/test-quiet.sh | 18 +++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 01988c8..21fbfe8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -40,4 +40,5 @@ repos: pass_filenames: false always_run: true stages: [pre-push] + verbose: true diff --git a/scripts/test-quiet.sh b/scripts/test-quiet.sh index 0156092..2f4ccc9 100755 --- a/scripts/test-quiet.sh +++ b/scripts/test-quiet.sh @@ -12,24 +12,24 @@ if [ $CTEST_EXIT -ne 0 ]; then exit 1 fi -# Extract summary from ctest output -echo "C++/CUDA tests:" -echo "$CTEST_OUTPUT" | grep -E "(tests passed|Test #.*Passed)" | head -2 | sed 's/^/ /' +# Extract summary from ctest output (output to stderr so pre-commit shows it) +echo "C++/CUDA tests:" >&2 +echo "$CTEST_OUTPUT" | grep -E "(tests passed|Test #.*Passed)" | head -2 | sed 's/^/ /' >&2 # Run pytest with quiet flag PYTEST_OUTPUT=$(pixi run pytest --quiet 2>&1) PYTEST_EXIT=$? if [ $PYTEST_EXIT -ne 0 ]; then - echo "" - echo "Python tests:" - echo "$PYTEST_OUTPUT" + echo "" >&2 + echo "Python tests:" >&2 + echo "$PYTEST_OUTPUT" >&2 exit 1 fi -# Extract summary from pytest output (quiet mode shows minimal output) -echo "Python tests:" -echo "$PYTEST_OUTPUT" | grep -E "(passed|failed)" | tail -1 | sed 's/^/ /' +# Extract summary from pytest output (output to stderr so pre-commit shows it) +echo "Python tests:" >&2 +echo "$PYTEST_OUTPUT" | grep -E "(passed|failed)" | tail -1 | sed 's/^/ /' >&2 exit 0 From c3c7789348b9b17b3db01ef74812a26992db24d2 Mon Sep 17 00:00:00 2001 From: Arijit Dasgupta Date: Sun, 9 Nov 2025 07:59:31 +0000 Subject: [PATCH 06/12] setup of format and lint CI --- .github/workflows/ci.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..0087da0 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,34 @@ +name: Format and Lint + +on: + push: + branches: + - '**' # Run on push to any branch + pull_request: + branches: + - main + - master + +jobs: + format-and-lint: + name: Format and Lint Check + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install pixi + uses: prefix-dev/setup-pixi@v0.9.0 + with: + pixi-version: "latest" + + - name: Install dependencies + run: pixi install --all-environments + + - name: Check formatting + run: pixi run format-check + + - name: Lint code + run: pixi run lint + From 2acfaa65730633ec24aa055eb9084ab4fc2142e0 Mon Sep 17 00:00:00 2001 From: Arijit Dasgupta Date: Sun, 9 Nov 2025 08:08:47 +0000 Subject: [PATCH 07/12] remove duplicate CI actions for PR+branch, speed up pixi install, fix install bug in ci --- .github/workflows/ci.yml | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0087da0..00dda95 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,12 @@ on: - main - master +# Prevent duplicate runs when pushing to a branch with an open PR +# Groups PRs and their corresponding branch pushes together +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.head.ref || github.ref_name }} + cancel-in-progress: true + jobs: format-and-lint: name: Format and Lint Check @@ -22,9 +28,21 @@ jobs: uses: prefix-dev/setup-pixi@v0.9.0 with: pixi-version: "latest" + cache: ${{ github.event_name == 'pull_request' }} # Only cache on PRs + + - name: Cache pixi environment + if: github.event_name == 'pull_request' + uses: actions/cache@v4 + with: + path: | + ~/.pixi + .pixi + key: ${{ runner.os }}-pixi-${{ hashFiles('pixi.lock') }} + restore-keys: | + ${{ runner.os }}-pixi- - name: Install dependencies - run: pixi install --all-environments + run: pixi install - name: Check formatting run: pixi run format-check From d369b5e7df304a8a7d5a2c52851f8b532984b75f Mon Sep 17 00:00:00 2001 From: Arijit Dasgupta Date: Sun, 9 Nov 2025 08:18:05 +0000 Subject: [PATCH 08/12] CI triggers on PR only, not on pushes anymore --- .github/workflows/ci.yml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00dda95..6b028a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,20 +1,11 @@ name: Format and Lint on: - push: - branches: - - '**' # Run on push to any branch pull_request: branches: - main - master -# Prevent duplicate runs when pushing to a branch with an open PR -# Groups PRs and their corresponding branch pushes together -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.head.ref || github.ref_name }} - cancel-in-progress: true - jobs: format-and-lint: name: Format and Lint Check @@ -28,10 +19,9 @@ jobs: uses: prefix-dev/setup-pixi@v0.9.0 with: pixi-version: "latest" - cache: ${{ github.event_name == 'pull_request' }} # Only cache on PRs + cache: true # Cache pixi installation - name: Cache pixi environment - if: github.event_name == 'pull_request' uses: actions/cache@v4 with: path: | From 9b6002cee52ff96374aaa8bf63fa4d1d9a3fd3f0 Mon Sep 17 00:00:00 2001 From: Arijit Dasgupta Date: Sun, 9 Nov 2025 08:27:47 +0000 Subject: [PATCH 09/12] fixed readme --- README.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 916658f..0877ecb 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ Let's get the ball rolling with blazing fast CUDA kernels! ## Installation -For initial installation, run: +### Usage Setup + +To simply run `genmetaballs`: ```bash pixi install @@ -12,20 +14,14 @@ pixi install ### Development Setup -To install and use GenMetaBalls: - -```bash -pixi install -``` - -For development (formatting, linting, git hooks, testing): +For development: ```bash pixi install pixi run dev-setup ``` -This sets up [pre-commit](https://pre-commit.com/) git hooks: +The `dev-setup` task sets up [pre-commit](https://pre-commit.com/) git hooks: - **Pre-commit**: Formats and lints code before each commit - **Pre-push**: Runs all tests before pushes @@ -34,7 +30,7 @@ Run the hooks manually if needed: pixi run pre-commit-run # Run formatting/linting checks pixi run pre-commit-run-push # Run all tests ``` -``` + ## Testing From 3b6a29ce2ce40de383e2243f027892f6b9676e81 Mon Sep 17 00:00:00 2001 From: Arijit Dasgupta Date: Sun, 16 Nov 2025 21:18:13 +0000 Subject: [PATCH 10/12] reduced commands and number of files --- .pre-commit-config.yaml | 9 - README.md | 29 +-- pixi.lock | 2 +- pyproject.toml | 32 +--- scripts/fix.sh | 100 ++++++++++ scripts/{format/python.sh => format.sh} | 90 ++++++--- scripts/format/all.sh | 36 ---- scripts/format/check-all.sh | 39 ---- scripts/format/cpp.sh | 95 ---------- scripts/lint.sh | 238 ++++++++++++++++++++++++ scripts/lint/all.sh | 39 ---- scripts/lint/cpp-fix.sh | 48 ----- scripts/lint/cpp.sh | 122 ------------ scripts/lint/fix-all.sh | 36 ---- scripts/lint/python-fix.sh | 42 ----- scripts/lint/python.sh | 110 ----------- 16 files changed, 411 insertions(+), 656 deletions(-) create mode 100755 scripts/fix.sh rename scripts/{format/python.sh => format.sh} (50%) delete mode 100755 scripts/format/all.sh delete mode 100755 scripts/format/check-all.sh delete mode 100755 scripts/format/cpp.sh create mode 100755 scripts/lint.sh delete mode 100755 scripts/lint/all.sh delete mode 100755 scripts/lint/cpp-fix.sh delete mode 100755 scripts/lint/cpp.sh delete mode 100755 scripts/lint/fix-all.sh delete mode 100755 scripts/lint/python-fix.sh delete mode 100755 scripts/lint/python.sh diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 21fbfe8..14fdd04 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,15 +14,6 @@ repos: always_run: true stages: [pre-commit] - # Check formatting (C++/CUDA + Python) - - id: format-check - name: Check formatting - entry: pixi run format-check - language: system - pass_filenames: false - always_run: true - stages: [pre-commit] - # Lint files (C++/CUDA + Python) - id: lint name: Lint files diff --git a/README.md b/README.md index 0877ecb..9f67871 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,7 @@ The `dev-setup` task sets up [pre-commit](https://pre-commit.com/) git hooks: - **Pre-commit**: Formats and lints code before each commit - **Pre-push**: Runs all tests before pushes -Run the hooks manually if needed: -```bash -pixi run pre-commit-run # Run formatting/linting checks -pixi run pre-commit-run-push # Run all tests -``` +The `dev-setup` task also generates `compile_commands.json` (needed for accurate C++/CUDA linting). ## Testing @@ -60,34 +56,21 @@ pixi run test ## Formatting & Linting -### Formatting +All commands work on both C++/CUDA and Python files automatically. -Format all files (C++/CUDA + Python): +Format all files: ```bash pixi run format ``` -Check formatting without modifying files: -```bash -pixi run format-check -``` - -### Linting - Lint all files: ```bash pixi run lint ``` -Auto-fix linting issues: +Auto-fix linting issues and format files: ```bash -pixi run lint-fix +pixi run fix ``` -**Language-specific commands:** -- `format-cpp`, `format-python` - Format specific language -- `format-check-cpp`, `format-check-python` - Check formatting for specific language -- `lint-cpp`, `lint-python` - Lint specific language -- `lint-cpp-fix`, `lint-python-fix` - Auto-fix linting issues for specific language -- `lint-full` - C++/CUDA linting with compile_commands.json (more accurate) -- `lint-full-fix` - Auto-fix C++/CUDA issues using compile_commands.json +These commands are automatically run by the pre-commit hooks when you commit code. diff --git a/pixi.lock b/pixi.lock index 4e4ae37..829429c 100644 --- a/pixi.lock +++ b/pixi.lock @@ -1184,7 +1184,7 @@ packages: - pypi: ./ name: genmetaballs version: 0.0.1 - sha256: 26221218d66c402421decc9e58527a501efb48874693ee046ce950bb972b500d + sha256: 126cf8632a1b1b1d420ec7aba40c4914995cec878ec28ee740ae6ba2d219fbc1 requires_dist: - numpy requires_python: '>=3.13' diff --git a/pyproject.toml b/pyproject.toml index d44cf8e..9fe5d45 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,32 +41,12 @@ ctest = "ctest --test-dir build" pytest = "pytest" test = { depends-on = ["ctest", "pytest"] } compile-commands = "cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON" -# Combined formatting and linting (C++/CUDA + Python) -format = { cmd = "scripts/format/all.sh" } -format-check = { cmd = "scripts/format/check-all.sh" } -lint = { cmd = "scripts/lint/all.sh" } -lint-fix = { cmd = "scripts/lint/fix-all.sh" } -# C++/CUDA only -format-cpp = { cmd = "scripts/format/cpp.sh" } -format-check-cpp = { cmd = "scripts/format/cpp.sh check" } -lint-cpp = { cmd = "scripts/lint/cpp.sh" } -lint-cpp-fix = { cmd = "scripts/lint/cpp-fix.sh" } -# lint-full: Uses compile_commands.json for more accurate C++/CUDA linting -# (includes proper include paths, defines, and compiler flags from CMake) -lint-full = { cmd = "scripts/lint/cpp.sh full" } -lint-full-fix = { cmd = "scripts/lint/cpp-fix.sh full" } -# Python only -format-python = { cmd = "scripts/format/python.sh" } -format-check-python = { cmd = "scripts/format/python.sh check" } -lint-python = { cmd = "scripts/lint/python.sh" } -lint-python-fix = { cmd = "scripts/lint/python-fix.sh" } -# Git hooks (using pre-commit framework) -install-hooks = { cmd = "pre-commit install && pre-commit install --hook-type pre-push" } -# Dev setup: install git hooks (for development workflow) -dev-setup = { cmd = "pre-commit install && pre-commit install --hook-type pre-push" } -# Pre-commit tasks -pre-commit-run = { cmd = "pre-commit run --all-files" } -pre-commit-run-push = { cmd = "scripts/test-quiet.sh" } +# Simple lint/format/fix commands (C++/CUDA + Python) +format = { cmd = "scripts/format.sh" } +lint = { cmd = "scripts/lint.sh" } +fix = { cmd = "scripts/fix.sh" } +# Dev setup: install git hooks and generate compile_commands.json +dev-setup = { depends-on = ["compile-commands"], cmd = "pre-commit install && pre-commit install --hook-type pre-push" } [tool.pixi.environments] default = { solve-group = "default", features = ["dev"] } diff --git a/scripts/fix.sh b/scripts/fix.sh new file mode 100755 index 0000000..33c62bc --- /dev/null +++ b/scripts/fix.sh @@ -0,0 +1,100 @@ +#!/bin/bash +set -e + +echo "๐Ÿ”ง Auto-fixing linting issues and formatting all files (C++/CUDA + Python)..." +echo "" + +# Fix C++/CUDA files +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +CPP_FILES=$(find genmetaballs/src/cuda tests -type f \( -name '*.cu' -o -name '*.cuh' \) 2>/dev/null || true) + +if [ -z "$CPP_FILES" ]; then + echo "โœ“ No CUDA/C++ files found" + CPP_EXIT=0 +else + USE_COMPILE_COMMANDS=false + + # Try to use compile_commands.json if available + if [ -f "build/compile_commands.json" ]; then + USE_COMPILE_COMMANDS=true + else + echo "โš ๏ธ compile_commands.json not found. Running 'pixi run compile-commands' first..." + cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON >/dev/null 2>&1 || true + if [ -f "build/compile_commands.json" ]; then + USE_COMPILE_COMMANDS=true + fi + fi + + echo "๐Ÿ”ง Auto-fixing linting issues in CUDA/C++ files..." + FIXED=0 + + while IFS= read -r file; do + if [ -f "$file" ]; then + if [ "$USE_COMPILE_COMMANDS" = true ]; then + # Use compile_commands.json for better analysis + OUTPUT=$(clang-tidy --fix "$file" 2>&1 || true) + else + # Use manual flags + OUTPUT=$(clang-tidy --fix "$file" -- -Igenmetaballs/src/cuda -std=c++20 2>&1 || true) + fi + + # Check if any fixes were applied (clang-tidy modifies files in place) + echo " โœ“ Processed: $file" + FIXED=1 + fi + done <<< "$CPP_FILES" + + echo "โœจ C++/CUDA auto-fix complete! ($(echo "$CPP_FILES" | wc -l) files processed)" + CPP_EXIT=0 +fi + +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + +# Fix Python files +PYTHON_FILES=$(find genmetaballs tests -type f -name '*.py' 2>/dev/null | sort || true) + +if [ -z "$PYTHON_FILES" ]; then + echo "โœ“ No Python files found" + PYTHON_EXIT=0 +else + echo "๐Ÿ”ง Auto-fixing linting issues in Python files..." + FIXED_COUNT=0 + ISSUE_COUNT=0 + + # Run ruff check with --fix + RUFF_OUTPUT=$(ruff check --fix $PYTHON_FILES 2>&1 || true) + RUFF_EXIT=$? + + # Process each file to show what was fixed + while IFS= read -r file; do + if [ -f "$file" ]; then + # Check if this file had any issues + FILE_ISSUES=$(echo "$RUFF_OUTPUT" | grep "^$file" || true) + if [ -z "$FILE_ISSUES" ]; then + echo " โœ“ $file (no issues)" + else + echo " โœ— $file (fixed issues)" + echo "$FILE_ISSUES" | sed 's/^/ /' + ISSUE_COUNT=$((ISSUE_COUNT + 1)) + fi + FIXED_COUNT=$((FIXED_COUNT + 1)) + fi + done <<< "$PYTHON_FILES" + + if [ $RUFF_EXIT -eq 0 ] && [ $ISSUE_COUNT -eq 0 ]; then + echo "โœจ All Python files are clean! ($FIXED_COUNT files checked)" + else + echo "โœจ Python auto-fix complete! ($ISSUE_COUNT files had fixes applied)" + fi + PYTHON_EXIT=0 +fi + +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +echo "โœจ Auto-fix complete! Now formatting files..." +echo "" + +# Now format all files +exec scripts/format.sh + diff --git a/scripts/format/python.sh b/scripts/format.sh similarity index 50% rename from scripts/format/python.sh rename to scripts/format.sh index c02a8f4..1e738df 100755 --- a/scripts/format/python.sh +++ b/scripts/format.sh @@ -1,52 +1,74 @@ #!/bin/bash set -e -PYTHON_FILES=$(find genmetaballs tests -type f -name '*.py' 2>/dev/null | sort || true) +echo "๐Ÿ”ง Formatting all files (C++/CUDA + Python)..." +echo "" -if [ -z "$PYTHON_FILES" ]; then - echo "โœ“ No Python files found" - exit 0 -fi +# Format C++/CUDA files +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +CPP_FILES=$(find genmetaballs/src/cuda tests -type f \( -name '*.cu' -o -name '*.cuh' \) 2>/dev/null || true) -if [ "$1" = "check" ]; then - echo "๐Ÿ” Checking Python file formatting..." - NEEDS_FORMAT=0 - - # First check import ordering - ruff check --select I --fix --quiet $PYTHON_FILES >/dev/null 2>&1 || true - - # Then check formatting +if [ -z "$CPP_FILES" ]; then + echo "โœ“ No CUDA/C++ files found" + CPP_EXIT=0 +else + echo "๐Ÿ”ง Formatting CUDA/C++ files..." + FORMATTED=0 + ALREADY_FORMATTED=0 OLD_IFS="$IFS" IFS=$'\n' - for file in $PYTHON_FILES; do + for file in $CPP_FILES; do IFS="$OLD_IFS" if [ -f "$file" ]; then + # Check if file needs formatting set +e - ERROR_OUTPUT=$(ruff format --check "$file" 2>&1) - EXIT_CODE=$? + clang-format --dry-run --Werror "$file" >/dev/null 2>&1 + CHECK_EXIT=$? set -e - if [ $EXIT_CODE -eq 0 ]; then - echo " โœ“ $file" + if [ $CHECK_EXIT -eq 0 ]; then + echo " โœ“ Already formatted: $file" + ALREADY_FORMATTED=1 else - echo " โœ— $file needs formatting" - if [ -n "$ERROR_OUTPUT" ]; then - echo "$ERROR_OUTPUT" | sed 's/^/ /' + set +e + ERROR_OUTPUT=$(clang-format -i "$file" 2>&1) + EXIT_CODE=$? + set -e + if [ $EXIT_CODE -eq 0 ]; then + echo " โœ“ Formatted: $file" + FORMATTED=1 else - echo " File does not match ruff format style" + echo " โœ— Failed to format: $file" + if [ -n "$ERROR_OUTPUT" ]; then + echo "$ERROR_OUTPUT" | sed 's/^/ /' + else + echo " Unknown formatting error" + fi + echo "" + echo "๐Ÿ’ก Review the error above and fix the issues, then run 'pixi run format' again" + exit 1 fi - NEEDS_FORMAT=1 fi fi done IFS="$OLD_IFS" - if [ $NEEDS_FORMAT -eq 0 ]; then - echo "โœจ All Python files are properly formatted!" - exit 0 - else - echo "โŒ Some Python files need formatting. Run 'pixi run format' to fix." - exit 1 + if [ $FORMATTED -eq 1 ]; then + echo "โœจ C++/CUDA formatting complete!" + elif [ $ALREADY_FORMATTED -eq 1 ]; then + echo "โœจ All CUDA/C++ files were already formatted!" fi + CPP_EXIT=0 +fi + +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + +# Format Python files +PYTHON_FILES=$(find genmetaballs tests -type f -name '*.py' 2>/dev/null | sort || true) + +if [ -z "$PYTHON_FILES" ]; then + echo "โœ“ No Python files found" + PYTHON_EXIT=0 else echo "๐Ÿ”ง Formatting Python files..." FORMATTED=0 @@ -77,7 +99,6 @@ else FORMATTED=1 else echo " โœ— Failed to format: $file" - # Show full error output with file and line details if [ -n "$ERROR_OUTPUT" ]; then echo "$ERROR_OUTPUT" | sed 's/^/ /' else @@ -100,5 +121,14 @@ else elif [ $ALREADY_FORMATTED -eq 1 ]; then echo "โœจ All Python files were already formatted!" fi + PYTHON_EXIT=0 +fi + +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +echo "โœจ Formatting complete!" + +if [ ${CPP_EXIT:-0} -ne 0 ] || [ ${PYTHON_EXIT:-0} -ne 0 ]; then + exit 1 fi diff --git a/scripts/format/all.sh b/scripts/format/all.sh deleted file mode 100755 index 6196c45..0000000 --- a/scripts/format/all.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -set -e - -echo "๐Ÿ”ง Formatting all files (C++/CUDA + Python)..." -echo "" - -# Format C++/CUDA files -echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" -if [ -f "scripts/format/cpp.sh" ]; then - scripts/format/cpp.sh - CPP_EXIT=$? -else - echo "โš ๏ธ C++/CUDA format script not found" - CPP_EXIT=0 -fi - -echo "" -echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" - -# Format Python files -if [ -f "scripts/format/python.sh" ]; then - scripts/format/python.sh - PYTHON_EXIT=$? -else - echo "โš ๏ธ Python format script not found" - PYTHON_EXIT=0 -fi - -echo "" -echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" -echo "โœจ Formatting complete!" - -if [ $CPP_EXIT -ne 0 ] || [ $PYTHON_EXIT -ne 0 ]; then - exit 1 -fi - diff --git a/scripts/format/check-all.sh b/scripts/format/check-all.sh deleted file mode 100755 index 22596ea..0000000 --- a/scripts/format/check-all.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -set -e - -echo "๐Ÿ” Checking formatting for all files (C++/CUDA + Python)..." -echo "" - -# Check C++/CUDA files -echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" -if [ -f "scripts/format/cpp.sh" ]; then - scripts/format/cpp.sh check - CPP_EXIT=$? -else - echo "โš ๏ธ C++/CUDA format script not found" - CPP_EXIT=0 -fi - -echo "" -echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" - -# Check Python files -if [ -f "scripts/format/python.sh" ]; then - scripts/format/python.sh check - PYTHON_EXIT=$? -else - echo "โš ๏ธ Python format script not found" - PYTHON_EXIT=0 -fi - -echo "" -echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" - -if [ $CPP_EXIT -eq 0 ] && [ $PYTHON_EXIT -eq 0 ]; then - echo "โœจ All files are properly formatted!" - exit 0 -else - echo "โŒ Some files need formatting. Run 'pixi run format' to fix." - exit 1 -fi - diff --git a/scripts/format/cpp.sh b/scripts/format/cpp.sh deleted file mode 100755 index 1c37cfe..0000000 --- a/scripts/format/cpp.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/bin/bash -set -e - -FILES=$(find genmetaballs/src/cuda tests -type f \( -name '*.cu' -o -name '*.cuh' \) 2>/dev/null || true) - -if [ -z "$FILES" ]; then - echo "โœ“ No CUDA/C++ files found" - exit 0 -fi - -if [ "$1" = "check" ]; then - echo "๐Ÿ” Checking CUDA/C++ file formatting..." - NEEDS_FORMAT=0 - OLD_IFS="$IFS" - IFS=$'\n' - for file in $FILES; do - IFS="$OLD_IFS" - if [ -f "$file" ]; then - # Check formatting: capture both stdout and stderr for detailed error messages - set +e # Temporarily disable exit on error to capture exit code - ERROR_OUTPUT=$(clang-format --dry-run --Werror "$file" 2>&1) - EXIT_CODE=$? - set -e # Re-enable exit on error - if [ $EXIT_CODE -eq 0 ]; then - echo " โœ“ $file" - else - echo " โœ— $file needs formatting" - # Show full error output with file and line details - if [ -n "$ERROR_OUTPUT" ]; then - echo "$ERROR_OUTPUT" | sed 's/^/ /' - else - echo " File does not match clang-format style" - fi - NEEDS_FORMAT=1 - fi - fi - done - IFS="$OLD_IFS" - - if [ $NEEDS_FORMAT -eq 0 ]; then - echo "โœจ All CUDA/C++ files are properly formatted!" - exit 0 - else - echo "โŒ Some files need formatting. Run 'pixi run format' to fix." - exit 1 - fi -else - echo "๐Ÿ”ง Formatting CUDA/C++ files..." - FORMATTED=0 - ALREADY_FORMATTED=0 - OLD_IFS="$IFS" - IFS=$'\n' - for file in $FILES; do - IFS="$OLD_IFS" - if [ -f "$file" ]; then - # Check if file needs formatting - set +e - clang-format --dry-run --Werror "$file" >/dev/null 2>&1 - CHECK_EXIT=$? - set -e - if [ $CHECK_EXIT -eq 0 ]; then - echo " โœ“ Already formatted: $file" - ALREADY_FORMATTED=1 - else - set +e - ERROR_OUTPUT=$(clang-format -i "$file" 2>&1) - EXIT_CODE=$? - set -e - if [ $EXIT_CODE -eq 0 ]; then - echo " โœ“ Formatted: $file" - FORMATTED=1 - else - echo " โœ— Failed to format: $file" - # Show full error output with file and line details - if [ -n "$ERROR_OUTPUT" ]; then - echo "$ERROR_OUTPUT" | sed 's/^/ /' - else - echo " Unknown formatting error" - fi - echo "" - echo "๐Ÿ’ก Review the error above and fix the issues, then run 'pixi run format' again" - exit 1 - fi - fi - fi - done - IFS="$OLD_IFS" - - if [ $FORMATTED -eq 1 ]; then - echo "โœจ Formatting complete!" - elif [ $ALREADY_FORMATTED -eq 1 ]; then - echo "โœจ All CUDA/C++ files were already formatted!" - fi -fi - diff --git a/scripts/lint.sh b/scripts/lint.sh new file mode 100755 index 0000000..69f1ba9 --- /dev/null +++ b/scripts/lint.sh @@ -0,0 +1,238 @@ +#!/bin/bash +set -e + +echo "๐Ÿ” Linting all files (C++/CUDA + Python)..." +echo "" + +# Lint C++/CUDA files +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" +CPP_FILES=$(find genmetaballs/src/cuda tests -type f \( -name '*.cu' -o -name '*.cuh' \) 2>/dev/null || true) + +if [ -z "$CPP_FILES" ]; then + echo "โœ“ No CUDA/C++ files found" + CPP_EXIT=0 +else + USE_COMPILE_COMMANDS=true + + # Try to use compile_commands.json if available (faster and more accurate) + if [ ! -f "build/compile_commands.json" ]; then + echo "โš ๏ธ compile_commands.json not found. Generating it..." + cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON >/dev/null 2>&1 || true + if [ ! -f "build/compile_commands.json" ]; then + echo "โš ๏ธ Could not generate compile_commands.json, falling back to basic mode" + USE_COMPILE_COMMANDS=false + fi + fi + + echo "๐Ÿ” Linting CUDA/C++ files..." + HAS_ISSUES=0 + CLEAN_FILES=0 + FILES_WITH_ISSUES=0 + + # Create temporary directory for parallel processing results + TMPDIR=$(mktemp -d) + trap "rm -rf $TMPDIR" EXIT + + # Export variables for parallel execution + export USE_COMPILE_COMMANDS + export TMPDIR + + # Run clang-tidy in parallel (use number of CPU cores) + NPROC=$(nproc 2>/dev/null || echo 4) + echo "$CPP_FILES" | xargs -P "$NPROC" -I {} sh -c ' + file="$1" + use_compile_commands="$2" + tmpdir="$3" + + if [ ! -f "$file" ]; then + exit 0 + fi + + if [ "$use_compile_commands" = "true" ]; then + OUTPUT=$(clang-tidy "$file" 2>&1 || true) + else + OUTPUT=$(clang-tidy "$file" -- -Igenmetaballs/src/cuda -std=c++20 2>&1 || true) + fi + + FILE_BASENAME=$(basename "$file") + ISSUES=$(echo "$OUTPUT" | grep -E "(^|/)$FILE_BASENAME:" | \ + grep -E ": (warning|error):" | \ + grep -v "suppressed" | \ + grep -v "clang-diagnostic-error" | \ + grep -v "__clang_cuda_runtime_wrapper.h" | \ + grep -v "CUDA version is newer" | \ + grep -v "unable to handle compilation" | \ + grep -v "unused-command-line-argument" | \ + grep -v "linker.*input unused" | \ + grep -v "Error parsing.*clang-tidy" | \ + grep -v "Error while processing" | \ + grep -v "Found compiler error" || true) + + tmpfile="$tmpdir/$(basename "$file" | tr "/" "_").lint" + if [ -z "$ISSUES" ]; then + echo "CLEAN:$file" > "$tmpfile" + else + echo "ISSUES:$file" > "$tmpfile" + echo "$ISSUES" >> "$tmpfile" + fi + ' _ {} "$USE_COMPILE_COMMANDS" "$TMPDIR" + + # Process results in order + OLD_IFS="$IFS" + IFS=$'\n' + for file in $CPP_FILES; do + IFS="$OLD_IFS" + if [ -f "$file" ]; then + tmpfile="$TMPDIR/$(basename "$file" | tr "/" "_").lint" + if [ -f "$tmpfile" ]; then + FIRST_LINE=$(head -n 1 "$tmpfile") + if echo "$FIRST_LINE" | grep -q "^CLEAN:"; then + echo " โœ“ $file" + CLEAN_FILES=$((CLEAN_FILES + 1)) + elif echo "$FIRST_LINE" | grep -q "^ISSUES:"; then + echo " โœ— $file" + # Show issues with proper indentation + tail -n +2 "$tmpfile" | while IFS= read -r line; do + if echo "$line" | grep -qE ":[0-9]+:[0-9]+: (warning|error):"; then + echo "$line" | sed -E 's/^[^:]+:([0-9]+:[0-9]+: (warning|error):.*)/ \1/' + else + echo " $line" + fi + done + FILES_WITH_ISSUES=$((FILES_WITH_ISSUES + 1)) + HAS_ISSUES=1 + fi + fi + fi + done + IFS="$OLD_IFS" + + if [ $HAS_ISSUES -eq 0 ]; then + echo "โœจ All C++/CUDA files passed linting! ($CLEAN_FILES files checked)" + CPP_EXIT=0 + else + echo "โŒ Found issues in $FILES_WITH_ISSUES file(s). ($CLEAN_FILES files clean)" + CPP_EXIT=1 + fi +fi + +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + +# Lint Python files +PYTHON_FILES=$(find genmetaballs tests -type f -name '*.py' 2>/dev/null | sort || true) + +if [ -z "$PYTHON_FILES" ]; then + echo "โœ“ No Python files found" + PYTHON_EXIT=0 +else + echo "๐Ÿ” Linting Python files..." + HAS_ISSUES=0 + CLEAN_COUNT=0 + ISSUE_COUNT=0 + + # Run ruff check and capture output + set +e + RUFF_OUTPUT=$(ruff check $PYTHON_FILES 2>&1) + RUFF_EXIT=$? + set -e + + # If ruff found issues, show them grouped by file + if [ $RUFF_EXIT -ne 0 ] && [ -n "$RUFF_OUTPUT" ]; then + # Process each file + OLD_IFS="$IFS" + IFS=$'\n' + FILES_WITH_ISSUES="" + for file in $PYTHON_FILES; do + IFS="$OLD_IFS" + if [ -f "$file" ]; then + # Check if this file appears in the error output + if echo "$RUFF_OUTPUT" | grep -q "$file"; then + echo " โœ— $file" + # Extract error blocks for this file - from error code through help line + echo "$RUFF_OUTPUT" | python3 -c " +import sys +file = '$file' +lines = sys.stdin.readlines() +in_block = False +block_lines = [] +for i, line in enumerate(lines): + # Start of error block for our file + if file in line and '-->' in line: + # Print previous block if any + if block_lines: + for bl in block_lines: + print(' ' + bl.rstrip()) + block_lines = [] + in_block = True + # Find the error code line before this (look back up to 2 lines) + for j in range(max(0, i-2), i): + if j < len(lines) and lines[j].strip(): + stripped = lines[j].strip() + if len(stripped) >= 2 and stripped[0].isupper() and stripped[1].isdigit(): + block_lines.append(lines[j]) + break + block_lines.append(line) + elif in_block: + block_lines.append(line) + # End of block at help: line or next error code or Found + if 'help:' in line or (line.strip() and line[0].isupper() and line[1].isdigit() and file not in line): + in_block = False + for bl in block_lines: + print(' ' + bl.rstrip()) + block_lines = [] + elif 'Found' in line: + in_block = False + if block_lines: + for bl in block_lines: + print(' ' + bl.rstrip()) + block_lines = [] +if block_lines: + for bl in block_lines: + print(' ' + bl.rstrip()) +" + FILES_WITH_ISSUES="$FILES_WITH_ISSUES $file" + ISSUE_COUNT=$((ISSUE_COUNT + 1)) + HAS_ISSUES=1 + else + echo " โœ“ $file" + CLEAN_COUNT=$((CLEAN_COUNT + 1)) + fi + fi + done + IFS="$OLD_IFS" + else + # All files are clean + OLD_IFS="$IFS" + IFS=$'\n' + for file in $PYTHON_FILES; do + IFS="$OLD_IFS" + if [ -f "$file" ]; then + echo " โœ“ $file" + CLEAN_COUNT=$((CLEAN_COUNT + 1)) + fi + done + IFS="$OLD_IFS" + fi + + if [ $RUFF_EXIT -eq 0 ] && [ $HAS_ISSUES -eq 0 ]; then + TOTAL_FILES=$(echo "$PYTHON_FILES" | wc -l) + echo "โœจ All Python files passed linting! ($TOTAL_FILES files checked)" + PYTHON_EXIT=0 + else + echo "โŒ Found issues in $ISSUE_COUNT file(s). ($CLEAN_COUNT files clean)" + PYTHON_EXIT=1 + fi +fi + +echo "" +echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + +if [ ${CPP_EXIT:-0} -eq 0 ] && [ ${PYTHON_EXIT:-0} -eq 0 ]; then + echo "โœจ All files passed linting!" + exit 0 +else + echo "โŒ Found linting issues." + exit 1 +fi + diff --git a/scripts/lint/all.sh b/scripts/lint/all.sh deleted file mode 100755 index 0624493..0000000 --- a/scripts/lint/all.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -set -e - -echo "๐Ÿ” Linting all files (C++/CUDA + Python)..." -echo "" - -# Lint C++/CUDA files -echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" -if [ -f "scripts/lint/cpp.sh" ]; then - scripts/lint/cpp.sh - CPP_EXIT=$? -else - echo "โš ๏ธ C++/CUDA lint script not found" - CPP_EXIT=0 -fi - -echo "" -echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" - -# Lint Python files -if [ -f "scripts/lint/python.sh" ]; then - scripts/lint/python.sh - PYTHON_EXIT=$? -else - echo "โš ๏ธ Python lint script not found" - PYTHON_EXIT=0 -fi - -echo "" -echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" - -if [ $CPP_EXIT -eq 0 ] && [ $PYTHON_EXIT -eq 0 ]; then - echo "โœจ All files passed linting!" - exit 0 -else - echo "โŒ Found linting issues." - exit 1 -fi - diff --git a/scripts/lint/cpp-fix.sh b/scripts/lint/cpp-fix.sh deleted file mode 100755 index 1364657..0000000 --- a/scripts/lint/cpp-fix.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash -set -e - -FILES=$(find genmetaballs/src/cuda tests -type f \( -name '*.cu' -o -name '*.cuh' \) 2>/dev/null || true) - -if [ -z "$FILES" ]; then - echo "โœ“ No CUDA/C++ files found" - exit 0 -fi - -USE_COMPILE_COMMANDS=false -if [ "$1" = "full" ]; then - USE_COMPILE_COMMANDS=true - if [ ! -f "build/compile_commands.json" ]; then - echo "โš ๏ธ compile_commands.json not found. Running 'pixi run compile-commands' first..." - cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON >/dev/null 2>&1 || true - if [ ! -f "build/compile_commands.json" ]; then - echo "โŒ Failed to generate compile_commands.json" - exit 1 - fi - fi -fi - -echo "๐Ÿ”ง Auto-fixing linting issues in CUDA/C++ files..." -FIXED=0 -HAS_ISSUES=0 - -while IFS= read -r file; do - if [ -f "$file" ]; then - if [ "$USE_COMPILE_COMMANDS" = true ]; then - # Use compile_commands.json for better analysis - OUTPUT=$(clang-tidy --fix "$file" 2>&1 || true) - else - # Use manual flags - OUTPUT=$(clang-tidy --fix "$file" -- -Igenmetaballs/src/cuda -std=c++20 2>&1 || true) - fi - - # Check if any fixes were applied (clang-tidy modifies files in place) - # We can't easily detect what was fixed, so we'll just report the file was processed - echo " โœ“ Processed: $file" - FIXED=1 - fi -done <<< "$FILES" - -echo "" -echo "โœจ Auto-fix complete! ($(echo "$FILES" | wc -l) files processed)" -echo "๐Ÿ’ก Note: Review the changes and run 'pixi run lint' to check remaining issues" - diff --git a/scripts/lint/cpp.sh b/scripts/lint/cpp.sh deleted file mode 100755 index 1336dae..0000000 --- a/scripts/lint/cpp.sh +++ /dev/null @@ -1,122 +0,0 @@ -#!/bin/bash -set -e - -FILES=$(find genmetaballs/src/cuda tests -type f \( -name '*.cu' -o -name '*.cuh' \) 2>/dev/null || true) - -if [ -z "$FILES" ]; then - echo "โœ“ No CUDA/C++ files found" - exit 0 -fi - -USE_COMPILE_COMMANDS=true -if [ "$1" = "basic" ]; then - USE_COMPILE_COMMANDS=false -fi - -# Try to use compile_commands.json if available (faster and more accurate) -if [ "$USE_COMPILE_COMMANDS" = true ]; then - if [ ! -f "build/compile_commands.json" ]; then - echo "โš ๏ธ compile_commands.json not found. Generating it..." - cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON >/dev/null 2>&1 || true - if [ ! -f "build/compile_commands.json" ]; then - echo "โš ๏ธ Could not generate compile_commands.json, falling back to basic mode" - USE_COMPILE_COMMANDS=false - fi - fi -fi - -echo "๐Ÿ” Linting CUDA/C++ files..." -HAS_ISSUES=0 -CLEAN_FILES=0 -FILES_WITH_ISSUES=0 - -# Create temporary directory for parallel processing results -TMPDIR=$(mktemp -d) -trap "rm -rf $TMPDIR" EXIT - -# Export variables for parallel execution -export USE_COMPILE_COMMANDS -export TMPDIR - -# Run clang-tidy in parallel (use number of CPU cores) -NPROC=$(nproc 2>/dev/null || echo 4) -echo "$FILES" | xargs -P "$NPROC" -I {} sh -c ' - file="$1" - use_compile_commands="$2" - tmpdir="$3" - - if [ ! -f "$file" ]; then - exit 0 - fi - - if [ "$use_compile_commands" = "true" ]; then - OUTPUT=$(clang-tidy "$file" 2>&1 || true) - else - OUTPUT=$(clang-tidy "$file" -- -Igenmetaballs/src/cuda -std=c++20 2>&1 || true) - fi - - FILE_BASENAME=$(basename "$file") - ISSUES=$(echo "$OUTPUT" | grep -E "(^|/)$FILE_BASENAME:" | \ - grep -E ": (warning|error):" | \ - grep -v "suppressed" | \ - grep -v "clang-diagnostic-error" | \ - grep -v "__clang_cuda_runtime_wrapper.h" | \ - grep -v "CUDA version is newer" | \ - grep -v "unable to handle compilation" | \ - grep -v "unused-command-line-argument" | \ - grep -v "linker.*input unused" | \ - grep -v "Error parsing.*clang-tidy" | \ - grep -v "Error while processing" | \ - grep -v "Found compiler error" || true) - - tmpfile="$tmpdir/$(basename "$file" | tr "/" "_").lint" - if [ -z "$ISSUES" ]; then - echo "CLEAN:$file" > "$tmpfile" - else - echo "ISSUES:$file" > "$tmpfile" - echo "$ISSUES" >> "$tmpfile" - fi -' _ {} "$USE_COMPILE_COMMANDS" "$TMPDIR" - -# Process results in order -OLD_IFS="$IFS" -IFS=$'\n' -for file in $FILES; do - IFS="$OLD_IFS" - if [ -f "$file" ]; then - tmpfile="$TMPDIR/$(basename "$file" | tr "/" "_").lint" - if [ -f "$tmpfile" ]; then - FIRST_LINE=$(head -n 1 "$tmpfile") - if echo "$FIRST_LINE" | grep -q "^CLEAN:"; then - echo " โœ“ $file" - CLEAN_FILES=$((CLEAN_FILES + 1)) - elif echo "$FIRST_LINE" | grep -q "^ISSUES:"; then - echo " โœ— $file" - # Show issues with proper indentation - tail -n +2 "$tmpfile" | while IFS= read -r line; do - if echo "$line" | grep -qE ":[0-9]+:[0-9]+: (warning|error):"; then - echo "$line" | sed -E 's/^[^:]+:([0-9]+:[0-9]+: (warning|error):.*)/ \1/' - else - echo " $line" - fi - done - FILES_WITH_ISSUES=$((FILES_WITH_ISSUES + 1)) - HAS_ISSUES=1 - fi - fi - fi -done -IFS="$OLD_IFS" - -echo "" -if [ $HAS_ISSUES -eq 0 ]; then - echo "โœจ All files passed linting! ($CLEAN_FILES files checked)" - exit 0 -else - echo "โŒ Found issues in $FILES_WITH_ISSUES file(s). ($CLEAN_FILES files clean)" - if [ "$USE_COMPILE_COMMANDS" = false ]; then - echo "๐Ÿ’ก Tip: Run 'pixi run lint-full' for more accurate analysis using compile_commands.json" - fi - exit 1 -fi - diff --git a/scripts/lint/fix-all.sh b/scripts/lint/fix-all.sh deleted file mode 100755 index ca73da0..0000000 --- a/scripts/lint/fix-all.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -set -e - -echo "๐Ÿ”ง Auto-fixing linting issues in all files (C++/CUDA + Python)..." -echo "" - -# Fix C++/CUDA files -echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" -if [ -f "scripts/lint/cpp-fix.sh" ]; then - scripts/lint/cpp-fix.sh - CPP_EXIT=$? -else - echo "โš ๏ธ C++/CUDA lint-fix script not found" - CPP_EXIT=0 -fi - -echo "" -echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" - -# Fix Python files -if [ -f "scripts/lint/python-fix.sh" ]; then - scripts/lint/python-fix.sh - PYTHON_EXIT=$? -else - echo "โš ๏ธ Python lint-fix script not found" - PYTHON_EXIT=0 -fi - -echo "" -echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" -echo "โœจ Auto-fix complete!" - -if [ $CPP_EXIT -ne 0 ] || [ $PYTHON_EXIT -ne 0 ]; then - exit 1 -fi - diff --git a/scripts/lint/python-fix.sh b/scripts/lint/python-fix.sh deleted file mode 100755 index 3872859..0000000 --- a/scripts/lint/python-fix.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -set -e - -PYTHON_FILES=$(find genmetaballs tests -type f -name '*.py' 2>/dev/null | sort || true) - -if [ -z "$PYTHON_FILES" ]; then - echo "โœ“ No Python files found" - exit 0 -fi - -echo "๐Ÿ”ง Auto-fixing linting issues in Python files..." -FIXED_COUNT=0 -ISSUE_COUNT=0 - -# Run ruff check with --fix -RUFF_OUTPUT=$(ruff check --fix $PYTHON_FILES 2>&1 || true) -RUFF_EXIT=$? - -# Process each file to show what was fixed -while IFS= read -r file; do - if [ -f "$file" ]; then - # Check if this file had any issues - FILE_ISSUES=$(echo "$RUFF_OUTPUT" | grep "^$file" || true) - if [ -z "$FILE_ISSUES" ]; then - echo " โœ“ $file (no issues)" - else - echo " โœ— $file (fixed issues)" - echo "$FILE_ISSUES" | sed 's/^/ /' - ISSUE_COUNT=$((ISSUE_COUNT + 1)) - fi - FIXED_COUNT=$((FIXED_COUNT + 1)) - fi -done <<< "$PYTHON_FILES" - -echo "" -if [ $RUFF_EXIT -eq 0 ] && [ $ISSUE_COUNT -eq 0 ]; then - echo "โœจ All files are clean! ($FIXED_COUNT files checked)" -else - echo "โœจ Auto-fix complete! ($ISSUE_COUNT files had fixes applied)" - echo "๐Ÿ’ก Note: Some issues may require manual fixes. Run 'pixi run lint-python' to check remaining issues" -fi - diff --git a/scripts/lint/python.sh b/scripts/lint/python.sh deleted file mode 100755 index 56f35e8..0000000 --- a/scripts/lint/python.sh +++ /dev/null @@ -1,110 +0,0 @@ -#!/bin/bash -set -e - -PYTHON_FILES=$(find genmetaballs tests -type f -name '*.py' 2>/dev/null | sort || true) - -if [ -z "$PYTHON_FILES" ]; then - echo "โœ“ No Python files found" - exit 0 -fi - -echo "๐Ÿ” Linting Python files..." -HAS_ISSUES=0 -CLEAN_COUNT=0 -ISSUE_COUNT=0 - -# Run ruff check and capture output -set +e -RUFF_OUTPUT=$(ruff check $PYTHON_FILES 2>&1) -RUFF_EXIT=$? -set -e - -# If ruff found issues, show them grouped by file -if [ $RUFF_EXIT -ne 0 ] && [ -n "$RUFF_OUTPUT" ]; then - # Process each file - OLD_IFS="$IFS" - IFS=$'\n' - FILES_WITH_ISSUES="" - for file in $PYTHON_FILES; do - IFS="$OLD_IFS" - if [ -f "$file" ]; then - # Check if this file appears in the error output - if echo "$RUFF_OUTPUT" | grep -q "$file"; then - echo " โœ— $file" - # Extract error blocks for this file - from error code through help line - echo "$RUFF_OUTPUT" | python3 -c " -import sys -file = '$file' -lines = sys.stdin.readlines() -in_block = False -block_lines = [] -for i, line in enumerate(lines): - # Start of error block for our file - if file in line and '-->' in line: - # Print previous block if any - if block_lines: - for bl in block_lines: - print(' ' + bl.rstrip()) - block_lines = [] - in_block = True - # Find the error code line before this (look back up to 2 lines) - for j in range(max(0, i-2), i): - if j < len(lines) and lines[j].strip(): - stripped = lines[j].strip() - if len(stripped) >= 2 and stripped[0].isupper() and stripped[1].isdigit(): - block_lines.append(lines[j]) - break - block_lines.append(line) - elif in_block: - block_lines.append(line) - # End of block at help: line or next error code or Found - if 'help:' in line or (line.strip() and line[0].isupper() and line[1].isdigit() and file not in line): - in_block = False - for bl in block_lines: - print(' ' + bl.rstrip()) - block_lines = [] - elif 'Found' in line: - in_block = False - if block_lines: - for bl in block_lines: - print(' ' + bl.rstrip()) - block_lines = [] -if block_lines: - for bl in block_lines: - print(' ' + bl.rstrip()) -" - FILES_WITH_ISSUES="$FILES_WITH_ISSUES $file" - ISSUE_COUNT=$((ISSUE_COUNT + 1)) - HAS_ISSUES=1 - else - echo " โœ“ $file" - CLEAN_COUNT=$((CLEAN_COUNT + 1)) - fi - fi - done - IFS="$OLD_IFS" -else - # All files are clean - OLD_IFS="$IFS" - IFS=$'\n' - for file in $PYTHON_FILES; do - IFS="$OLD_IFS" - if [ -f "$file" ]; then - echo " โœ“ $file" - CLEAN_COUNT=$((CLEAN_COUNT + 1)) - fi - done - IFS="$OLD_IFS" -fi - -echo "" -if [ $RUFF_EXIT -eq 0 ] && [ $HAS_ISSUES -eq 0 ]; then - TOTAL_FILES=$(echo "$PYTHON_FILES" | wc -l) - echo "โœจ All Python files passed linting! ($TOTAL_FILES files checked)" - exit 0 -else - echo "โŒ Found issues in $ISSUE_COUNT file(s). ($CLEAN_COUNT files clean)" - echo "๐Ÿ’ก Tip: Run 'ruff check --fix' to auto-fix some issues" - exit 1 -fi - From 4ebd983d6b91fa22e0bff9e0d61d3e3e4bc6b123 Mon Sep 17 00:00:00 2001 From: Arijit Dasgupta Date: Sun, 16 Nov 2025 22:11:22 +0000 Subject: [PATCH 11/12] cleaned up compile-commands --- .github/workflows/ci.yml | 9 ++++++++- README.md | 1 - pixi.lock | 2 +- pyproject.toml | 5 ++--- scripts/fix.sh | 21 +-------------------- scripts/lint.sh | 24 +++--------------------- 6 files changed, 15 insertions(+), 47 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b028a1..bf7bf46 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,7 @@ name: Format and Lint on: pull_request: + types: [opened, synchronize, reopened] # Run when PR is opened, updated, or reopened branches: - main - master @@ -35,7 +36,13 @@ jobs: run: pixi install - name: Check formatting - run: pixi run format-check + run: | + pixi run format + if [ -n "$(git status --porcelain)" ]; then + echo "โŒ Files were not properly formatted. Run 'pixi run format' to fix." + git diff + exit 1 + fi - name: Lint code run: pixi run lint diff --git a/README.md b/README.md index 9f67871..7bd69b0 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,6 @@ The `dev-setup` task sets up [pre-commit](https://pre-commit.com/) git hooks: - **Pre-commit**: Formats and lints code before each commit - **Pre-push**: Runs all tests before pushes -The `dev-setup` task also generates `compile_commands.json` (needed for accurate C++/CUDA linting). ## Testing diff --git a/pixi.lock b/pixi.lock index 829429c..45ba381 100644 --- a/pixi.lock +++ b/pixi.lock @@ -1184,7 +1184,7 @@ packages: - pypi: ./ name: genmetaballs version: 0.0.1 - sha256: 126cf8632a1b1b1d420ec7aba40c4914995cec878ec28ee740ae6ba2d219fbc1 + sha256: ca600d699b71a3a2774ed25d156940135e5cee0f27335ec4e013dbabf63a7433 requires_dist: - numpy requires_python: '>=3.13' diff --git a/pyproject.toml b/pyproject.toml index 9fe5d45..9c79c32 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,13 +40,12 @@ pre-commit = ">=3.0.0" ctest = "ctest --test-dir build" pytest = "pytest" test = { depends-on = ["ctest", "pytest"] } -compile-commands = "cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON" # Simple lint/format/fix commands (C++/CUDA + Python) format = { cmd = "scripts/format.sh" } lint = { cmd = "scripts/lint.sh" } fix = { cmd = "scripts/fix.sh" } -# Dev setup: install git hooks and generate compile_commands.json -dev-setup = { depends-on = ["compile-commands"], cmd = "pre-commit install && pre-commit install --hook-type pre-push" } +# Dev setup: install git hooks +dev-setup = "pre-commit install && pre-commit install --hook-type pre-push" [tool.pixi.environments] default = { solve-group = "default", features = ["dev"] } diff --git a/scripts/fix.sh b/scripts/fix.sh index 33c62bc..891345e 100755 --- a/scripts/fix.sh +++ b/scripts/fix.sh @@ -12,31 +12,12 @@ if [ -z "$CPP_FILES" ]; then echo "โœ“ No CUDA/C++ files found" CPP_EXIT=0 else - USE_COMPILE_COMMANDS=false - - # Try to use compile_commands.json if available - if [ -f "build/compile_commands.json" ]; then - USE_COMPILE_COMMANDS=true - else - echo "โš ๏ธ compile_commands.json not found. Running 'pixi run compile-commands' first..." - cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON >/dev/null 2>&1 || true - if [ -f "build/compile_commands.json" ]; then - USE_COMPILE_COMMANDS=true - fi - fi - echo "๐Ÿ”ง Auto-fixing linting issues in CUDA/C++ files..." FIXED=0 while IFS= read -r file; do if [ -f "$file" ]; then - if [ "$USE_COMPILE_COMMANDS" = true ]; then - # Use compile_commands.json for better analysis - OUTPUT=$(clang-tidy --fix "$file" 2>&1 || true) - else - # Use manual flags - OUTPUT=$(clang-tidy --fix "$file" -- -Igenmetaballs/src/cuda -std=c++20 2>&1 || true) - fi + OUTPUT=$(clang-tidy --fix "$file" -- -Igenmetaballs/src/cuda -std=c++20 2>&1 || true) # Check if any fixes were applied (clang-tidy modifies files in place) echo " โœ“ Processed: $file" diff --git a/scripts/lint.sh b/scripts/lint.sh index 69f1ba9..074137c 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -12,18 +12,6 @@ if [ -z "$CPP_FILES" ]; then echo "โœ“ No CUDA/C++ files found" CPP_EXIT=0 else - USE_COMPILE_COMMANDS=true - - # Try to use compile_commands.json if available (faster and more accurate) - if [ ! -f "build/compile_commands.json" ]; then - echo "โš ๏ธ compile_commands.json not found. Generating it..." - cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON >/dev/null 2>&1 || true - if [ ! -f "build/compile_commands.json" ]; then - echo "โš ๏ธ Could not generate compile_commands.json, falling back to basic mode" - USE_COMPILE_COMMANDS=false - fi - fi - echo "๐Ÿ” Linting CUDA/C++ files..." HAS_ISSUES=0 CLEAN_FILES=0 @@ -34,25 +22,19 @@ else trap "rm -rf $TMPDIR" EXIT # Export variables for parallel execution - export USE_COMPILE_COMMANDS export TMPDIR # Run clang-tidy in parallel (use number of CPU cores) NPROC=$(nproc 2>/dev/null || echo 4) echo "$CPP_FILES" | xargs -P "$NPROC" -I {} sh -c ' file="$1" - use_compile_commands="$2" - tmpdir="$3" + tmpdir="$2" if [ ! -f "$file" ]; then exit 0 fi - if [ "$use_compile_commands" = "true" ]; then - OUTPUT=$(clang-tidy "$file" 2>&1 || true) - else - OUTPUT=$(clang-tidy "$file" -- -Igenmetaballs/src/cuda -std=c++20 2>&1 || true) - fi + OUTPUT=$(clang-tidy "$file" -- -Igenmetaballs/src/cuda -std=c++20 2>&1 || true) FILE_BASENAME=$(basename "$file") ISSUES=$(echo "$OUTPUT" | grep -E "(^|/)$FILE_BASENAME:" | \ @@ -75,7 +57,7 @@ else echo "ISSUES:$file" > "$tmpfile" echo "$ISSUES" >> "$tmpfile" fi - ' _ {} "$USE_COMPILE_COMMANDS" "$TMPDIR" + ' _ {} "$TMPDIR" # Process results in order OLD_IFS="$IFS" From f180271ae37657a334753310e004e7cf5633b6ab Mon Sep 17 00:00:00 2001 From: Arijit Dasgupta Date: Sun, 16 Nov 2025 22:20:10 +0000 Subject: [PATCH 12/12] simplified CI trigger condition --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf7bf46..a7c3efc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,6 @@ name: Format and Lint on: pull_request: - types: [opened, synchronize, reopened] # Run when PR is opened, updated, or reopened branches: - main - master