From b2a0b9c551799f7ba4c3d732cb145eb48dea7d5b Mon Sep 17 00:00:00 2001 From: Christopher Milan Date: Sun, 4 Jan 2026 19:38:12 -0800 Subject: [PATCH] autogen: dump patch in CI (#14010) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * autogen: don't fast-fail, produce patch artifact on differences All verification steps now use continue-on-error to run completely. Each job generates a patch artifact containing all differences found. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 * add gen from header test * fix tests * fail if diff * add forward decl autogen test * remove confusing/wrong comments * macos unittests set LIBCLANG_PATH --------- Co-authored-by: Claude Sonnet 4.5 --- .github/workflows/autogen.yml | 51 +++++++++++++++++++ .github/workflows/test.yml | 5 +- test/unit/test_autogen.py | 94 +++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 1 deletion(-) diff --git a/.github/workflows/autogen.yml b/.github/workflows/autogen.yml index c812588d14..a8989cdc85 100644 --- a/.github/workflows/autogen.yml +++ b/.github/workflows/autogen.yml @@ -40,11 +40,13 @@ jobs: - name: Install autogen support packages run: sudo apt-get install -y --no-install-recommends libclang-20-dev llvm-20-dev hip-dev libusb-1.0-0-dev - name: Verify OpenCL autogen + continue-on-error: true run: | mv tinygrad/runtime/autogen/opencl.py /tmp/opencl.py.bak python3 -c "from tinygrad.runtime.autogen import opencl" diff /tmp/opencl.py.bak tinygrad/runtime/autogen/opencl.py - name: Verify CUDA autogen + continue-on-error: true run: | mv tinygrad/runtime/autogen/cuda.py /tmp/cuda.py.bak mv tinygrad/runtime/autogen/nvrtc.py /tmp/nvrtc.py.bak @@ -58,6 +60,7 @@ jobs: diff /tmp/nv_570.py.bak tinygrad/runtime/autogen/nv_570.py diff /tmp/nv.py.bak tinygrad/runtime/autogen/nv.py - name: Verify AMD autogen + continue-on-error: true run: | mv tinygrad/runtime/autogen/comgr.py /tmp/comgr.py.bak mv tinygrad/runtime/autogen/hsa.py /tmp/hsa.py.bak @@ -89,6 +92,7 @@ jobs: diff /tmp/am_smu_v13_0_0.py.bak tinygrad/runtime/autogen/am/smu_v13_0_0.py diff /tmp/am_smu_v14_0_2.py.bak tinygrad/runtime/autogen/am/smu_v14_0_2.py - name: Verify Linux autogen + continue-on-error: true run: | mv tinygrad/runtime/autogen/libc.py /tmp/libc.py.bak mv tinygrad/runtime/autogen/kfd.py /tmp/kfd.py.bak @@ -104,16 +108,19 @@ jobs: diff /tmp/pci.py.bak tinygrad/runtime/autogen/pci.py diff /tmp/vfio.py.bak tinygrad/runtime/autogen/vfio.py - name: Verify LLVM autogen + continue-on-error: true run: | mv tinygrad/runtime/autogen/llvm.py /tmp/llvm.py.bak python3 -c "from tinygrad.runtime.autogen import llvm" diff /tmp/llvm.py.bak tinygrad/runtime/autogen/llvm.py - name: Verify WebGPU autogen + continue-on-error: true run: | mv tinygrad/runtime/autogen/webgpu.py /tmp/webgpu.py.bak python3 -c "from tinygrad.runtime.autogen import webgpu" diff /tmp/webgpu.py.bak tinygrad/runtime/autogen/webgpu.py - name: Verify Qualcomm autogen + continue-on-error: true run: | mv tinygrad/runtime/autogen/kgsl.py /tmp/kgsl.py.bak mv tinygrad/runtime/autogen/qcom_dsp.py /tmp/qcom_dsp.py.bak @@ -121,20 +128,36 @@ jobs: diff /tmp/kgsl.py.bak tinygrad/runtime/autogen/kgsl.py diff /tmp/qcom_dsp.py.bak tinygrad/runtime/autogen/qcom_dsp.py - name: Verify libusb autogen + continue-on-error: true run: | mv tinygrad/runtime/autogen/libusb.py /tmp/libusb.py.bak python3 -c "from tinygrad.runtime.autogen import libusb" diff /tmp/libusb.py.bak tinygrad/runtime/autogen/libusb.py - name: Verify mesa autogen + continue-on-error: true run: | mv tinygrad/runtime/autogen/mesa.py /tmp/mesa.py.bak python3 -c "from tinygrad.runtime.autogen import mesa" diff /tmp/mesa.py.bak tinygrad/runtime/autogen/mesa.py - name: Verify libclang autogen + continue-on-error: true run: | cp tinygrad/runtime/autogen/libclang.py /tmp/libclang.py.bak REGEN=1 python3 -c "from tinygrad.runtime.autogen import libclang" diff /tmp/libclang.py.bak tinygrad/runtime/autogen/libclang.py + - name: Generate patch for differences + run: | + if ! git diff --quiet; then + git diff > autogen-ubuntu.patch + fi + - name: Upload patch artifact + uses: actions/upload-artifact@v4 + with: + name: autogen-ubuntu-patch + path: autogen-ubuntu.patch + if-no-files-found: ignore + - name: Fail if differences found + run: git diff --quiet autogen-mac: name: In-tree Autogen (macos) runs-on: macos-14 @@ -147,10 +170,24 @@ jobs: with: llvm: 'true' - name: Verify macos autogen + continue-on-error: true run: | mv tinygrad/runtime/autogen/metal.py /tmp/metal.py.bak LIBCLANG_PATH=/opt/homebrew/opt/llvm@20/lib/libclang.dylib python3 -c "from tinygrad.runtime.autogen import metal" diff /tmp/metal.py.bak tinygrad/runtime/autogen/metal.py + - name: Generate patch for differences + run: | + if ! git diff --quiet; then + git diff > autogen-macos.patch + fi + - name: Upload patch artifact + uses: actions/upload-artifact@v4 + with: + name: autogen-macos-patch + path: autogen-macos.patch + if-no-files-found: ignore + - name: Fail if differences found + run: git diff --quiet autogen-comgr-3: name: In-tree Autogen (comgr 3) runs-on: ubuntu-24.04 @@ -170,7 +207,21 @@ jobs: sudo apt -qq update || true sudo apt-get install -y --no-install-recommends libclang-20-dev comgr - name: Verify comgr (3) autogen + continue-on-error: true run: | mv tinygrad/runtime/autogen/comgr_3.py /tmp/comgr_3.py.bak python3 -c "from tinygrad.runtime.autogen import comgr_3" diff /tmp/comgr_3.py.bak tinygrad/runtime/autogen/comgr_3.py + - name: Generate patch for differences + run: | + if ! git diff --quiet; then + git diff > autogen-comgr3.patch + fi + - name: Upload patch artifact + uses: actions/upload-artifact@v4 + with: + name: autogen-comgr3-patch + path: autogen-comgr3.patch + if-no-files-found: ignore + - name: Fail if differences found + run: git diff --quiet diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0790e3bd16..c8da280197 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -257,6 +257,7 @@ jobs: key: unittest-12 pydeps: "pillow numpy ftfy regex" deps: testing_unit + llvm: 'true' - name: Check Device.DEFAULT run: python -c "from tinygrad import Device; assert Device.DEFAULT == 'CPU', Device.DEFAULT" - name: Run unit tests @@ -309,7 +310,7 @@ jobs: deps: testing_unit python-version: '3.14' - name: Test SPEC=2 - run: SPEC=2 pytest --maxfail=10 -n auto --durations=30 --ignore=test/models --ignore test/test_custom_kernel.py --ignore test/unit/test_hashing.py --timeout 60 -k "not test_setitem_big" --splits 2 --group ${{ matrix.group }} + run: SPEC=2 pytest --maxfail=10 -n auto --durations=30 --ignore=test/models --ignore test/test_custom_kernel.py --ignore test/unit/test_hashing.py --ignore test/unit/test_autogen.py --timeout 60 -k "not test_setitem_big" --splits 2 --group ${{ matrix.group }} fuzzing: name: Fuzzing @@ -793,6 +794,8 @@ jobs: ocelot: 'true' llvm: 'true' - name: Run unit tests + env: + LIBCLANG_PATH: '/opt/homebrew/opt/llvm@20/lib/libclang.dylib' run: METAL=1 python -m pytest -n=auto test/unit/ --durations=20 - name: Run ONNX run: METAL=1 python -m pytest -n=auto test/external/external_test_onnx_backend.py --durations=20 diff --git a/test/unit/test_autogen.py b/test/unit/test_autogen.py index c04611cc54..ae7e9854b9 100644 --- a/test/unit/test_autogen.py +++ b/test/unit/test_autogen.py @@ -1,6 +1,7 @@ import ctypes, subprocess, tempfile, unittest from tinygrad.helpers import WIN from tinygrad.runtime.support.c import Struct +from tinygrad.runtime.support.autogen import gen class TestAutogen(unittest.TestCase): def test_packed_struct_sizeof(self): @@ -159,4 +160,97 @@ class TestAutogen(unittest.TestCase): assert ihdr.num_dies == 1 assert ihdr.base_addr_64_bit == 1 + @unittest.skipIf(WIN, "doesn't compile on windows") + def test_gen_from_header(self): + header_content = """ + typedef struct { + int x; + int y; + } Point; + + typedef enum { + RED = 0, + GREEN = 1, + BLUE = 2 + } Color; + + typedef struct { + Point origin; + int width; + int height; + Color color; + } Rectangle; + + int add_points(Point a, Point b); + """ + + with tempfile.NamedTemporaryFile(mode='w', suffix='.h') as f: + f.write(header_content) + f.flush() + + generated_code = gen(name="test_header", dll=None, files=[f.name]) + + namespace = {} + exec(generated_code, namespace) + + self.assertIn('Point', namespace) + self.assertIn('Color', namespace) + self.assertIn('Rectangle', namespace) + self.assertIn('RED', namespace) + self.assertIn('GREEN', namespace) + self.assertIn('BLUE', namespace) + + self.assertEqual(namespace['RED'], 0) + self.assertEqual(namespace['GREEN'], 1) + self.assertEqual(namespace['BLUE'], 2) + + Point = namespace['Point'] + p = Point() + self.assertIsInstance(p, Struct) + self.assertTrue(hasattr(p, 'x')) + self.assertTrue(hasattr(p, 'y')) + + Rectangle = namespace['Rectangle'] + rect = Rectangle() + self.assertTrue(hasattr(rect, 'origin')) + self.assertTrue(hasattr(rect, 'width')) + self.assertTrue(hasattr(rect, 'height')) + self.assertTrue(hasattr(rect, 'color')) + + @unittest.skipIf(WIN, "doesn't compile on windows") + def test_struct_ordering(self): + header_content = """ + struct A; + struct C; + typedef struct A A; + + struct B { + struct C *c_ptr; + }; + + struct C { + struct A *a_ptr; + }; + + struct A { + int x; + struct B *b_ptr; + }; + """ + with tempfile.NamedTemporaryFile(mode='w', suffix='.h') as f: + f.write(header_content) + f.flush() + generated_code = gen(name="test_ordering", dll=None, files=[f.name]) + namespace = {} + exec(generated_code, namespace) + self.assertIn('struct_A', namespace) + self.assertIn('struct_B', namespace) + self.assertIn('struct_C', namespace) + A, B, C = namespace['struct_A'], namespace['struct_B'], namespace['struct_C'] + a, b, c = A(), B(), C() + self.assertTrue(hasattr(a, 'x')) + self.assertTrue(hasattr(a, 'b_ptr')) + self.assertTrue(hasattr(b, 'c_ptr')) + self.assertTrue(hasattr(c, 'a_ptr')) + if __name__ == "__main__": unittest.main()