-
Notifications
You must be signed in to change notification settings - Fork 22
1800 lines (1498 loc) · 64.6 KB
/
release.yml
File metadata and controls
1800 lines (1498 loc) · 64.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
name: Build and Release
on:
push:
tags:
- 'v*.*.*' # Official release (v1.0.0)
- 'v*.*.*-beta*' # Beta release (v1.0.0-beta.1)
- 'v*.*.*-alpha*' # Alpha release (v1.0.0-alpha.1)
- 'v*.*.*-rc*' # Release Candidate (v1.0.0-rc.1)
# Add manual trigger option for testing
workflow_dispatch:
inputs:
version:
description: 'Version number (e.g.: v1.0.0-test)'
required: true
default: 'v0.1.0-test'
create_release:
description: 'Whether to create GitHub Release'
type: boolean
default: false
jobs:
# Run comprehensive tests before building release packages
test:
name: Run Full Test Suite
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true # Stop all jobs if any test fails
matrix:
include:
- os: windows-latest
name: Windows Tests
test_type: both
- os: ubuntu-latest
name: Linux Tests
test_type: both
- os: ubuntu-latest
name: Linux ARM64 Tests
test_type: functional
arch: arm64
- os: macos-latest
name: macOS Tests
test_type: both
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup cross-compilation for ARM64
if: matrix.arch == 'arm64'
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu qemu-user qemu-user-static \
libwayland-dev libxkbcommon-dev wayland-protocols \
libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev
- name: Setup Visual Studio (Windows)
if: matrix.os == 'windows-latest'
uses: microsoft/setup-msbuild@v1.1
- name: Install Linux dependencies
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y cmake build-essential gcc
if [ "${{ matrix.test_type }}" = "both" ]; then
sudo apt-get install -y libglfw3-dev \
libwayland-dev libxkbcommon-dev wayland-protocols \
libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev
fi
- name: Run Tests
shell: bash
run: |
cd scripts
echo "=== Running ${{ matrix.name }} ==="
if [ "${{ matrix.arch }}" = "arm64" ]; then
# ARM64 cross-compilation tests
echo "Running ARM64 functional tests..."
./run_tests.sh --functional --exit-when-failed
elif [ "${{ matrix.test_type }}" = "both" ]; then
# Full test suite (functional + performance)
echo "Running full test suite..."
./run_tests.sh --functional --exit-when-failed
./run_tests.sh --performance --exit-when-failed
else
# Functional tests only
echo "Running functional tests..."
./run_tests.sh --functional --exit-when-failed
fi
echo "✅ ${{ matrix.name }} completed successfully!"
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.name }}-${{ github.run_id }}
path: |
build/**/test_results.xml
build/**/*_results.xml
retention-days: 7
if-no-files-found: ignore
build:
name: Build Release Packages
needs: test
strategy:
fail-fast: false
matrix:
include:
# Static library builds (updated naming)
- os: macos-latest
name: macOS Static
artifact_name: ccap-macos-universal-static
build_type: Release
shared: false
- os: windows-latest
name: Windows Static
artifact_name: ccap-msvc-x86_64-static
build_type: Release
shared: false
- os: ubuntu-latest
name: Linux Static
artifact_name: ccap-linux-x86_64-static
build_type: Release
shared: false
- os: ubuntu-latest
name: Linux ARM64 Static
artifact_name: ccap-linux-arm64-static
build_type: Release
arch: arm64
shared: false
# Shared library builds (updated naming)
- os: macos-latest
name: macOS Shared
artifact_name: ccap-macos-universal-shared
build_type: Release
shared: true
- os: windows-latest
name: Windows Shared
artifact_name: ccap-msvc-x86_64-shared
build_type: Release
shared: true
- os: ubuntu-latest
name: Linux Shared
artifact_name: ccap-linux-x86_64-shared
build_type: Release
shared: true
- os: ubuntu-latest
name: Linux ARM64 Shared
artifact_name: ccap-linux-arm64-shared
build_type: Release
arch: arm64
shared: true
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup cross-compilation for ARM64
if: matrix.arch == 'arm64'
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \
libwayland-dev libxkbcommon-dev wayland-protocols \
libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev
- name: Setup Visual Studio (Windows)
if: matrix.os == 'windows-latest'
uses: microsoft/setup-msbuild@v1.1
- name: Configure CMake
shell: bash
run: |
# Set shared library flag based on matrix configuration
SHARED_FLAG=""
if [ "${{ matrix.shared }}" = "true" ]; then
SHARED_FLAG="-DCCAP_BUILD_SHARED=ON"
else
SHARED_FLAG="-DCCAP_BUILD_SHARED=OFF"
fi
if [ "${{ matrix.os }}" = "windows-latest" ]; then
# Windows: Use multi-config generator (Visual Studio)
cmake -B build -G "Visual Studio 17 2022" -A x64 -DCCAP_BUILD_EXAMPLES=ON -DCCAP_BUILD_TESTS=OFF $SHARED_FLAG
elif [ "${{ matrix.os }}" = "ubuntu-latest" ]; then
# Linux: Use single-config generator
if [ "${{ matrix.arch }}" = "arm64" ]; then
# ARM64 cross-compilation
cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
-DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \
-DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ \
-DCMAKE_SYSTEM_NAME=Linux \
-DCMAKE_SYSTEM_PROCESSOR=aarch64 \
-DCCAP_BUILD_EXAMPLES=ON -DCCAP_BUILD_TESTS=OFF $SHARED_FLAG
else
# Regular Linux x86_64
cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCCAP_BUILD_EXAMPLES=ON -DCCAP_BUILD_TESTS=OFF $SHARED_FLAG
fi
else
# macOS: Use single-config generator with universal binary
cmake -B build -DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCCAP_BUILD_EXAMPLES=ON -DCCAP_BUILD_TESTS=OFF $SHARED_FLAG
fi
- name: Build project
shell: bash
run: |
if [ "${{ matrix.os }}" = "windows-latest" ]; then
# Windows: Build both Debug and Release versions
cmake --build build --config Debug --parallel
cmake --build build --config Release --parallel
else
# Other platforms: Build specified version
cmake --build build --config ${{ matrix.build_type }} --parallel
fi
- name: List build outputs (Windows Debug)
if: matrix.os == 'windows-latest'
shell: bash
run: |
echo "=== Debug build outputs ==="
find build/Debug -name "*.lib" -o -name "*.dll" -o -name "*.exe" | head -20
echo "=== Release build outputs ==="
find build/Release -name "*.lib" -o -name "*.dll" -o -name "*.exe" | head -20
- name: Prepare package directory
shell: bash
run: |
mkdir -p package/lib
mkdir -p package/include
mkdir -p package/examples
mkdir -p package/cmake
- name: Copy libraries (Windows)
if: matrix.os == 'windows-latest'
shell: bash
run: |
# Copy Debug version library files (ccapd.lib generated through DEBUG_POSTFIX "d")
cp build/Debug/ccapd.lib package/lib/ccapd.lib || echo "Debug static library not found"
# Copy Release version library files, keeping ccap.lib name
cp build/Release/ccap.lib package/lib/ccap.lib || echo "Release static library not found"
# Copy shared libraries (if shared build)
if [ "${{ matrix.shared }}" = "true" ]; then
cp build/Debug/ccapd.dll package/lib/ccapd.dll || echo "Debug shared library not found"
cp build/Release/ccap.dll package/lib/ccap.dll || echo "Release shared library not found"
# Also copy import libraries for DLLs
cp build/Debug/ccapd.lib package/lib/ccapd_import.lib || echo "Debug import library not found"
cp build/Release/ccap.lib package/lib/ccap_import.lib || echo "Release import library not found"
fi
# Copy example programs (both versions in examples directory)
mkdir -p package/examples/Debug
mkdir -p package/examples/Release
cp build/Debug/*.exe package/examples/Debug/ || echo "Debug examples not found"
cp build/Release/*.exe package/examples/Release/ || echo "Release examples not found"
- name: Copy libraries (macOS)
if: matrix.os == 'macos-latest'
shell: bash
run: |
if [ "${{ matrix.shared }}" = "true" ]; then
# Copy shared library
cp build/libccap.dylib package/lib/ || echo "Shared library not found"
else
# Copy static library
cp build/libccap.a package/lib/ || echo "Static library not found"
fi
# Copy example programs
find build -name "*-print_camera" -exec cp {} package/examples/ \; || echo "Examples not found"
find build -name "*-minimal_example" -exec cp {} package/examples/ \; || echo "Examples not found"
find build -name "*-capture_grab" -exec cp {} package/examples/ \; || echo "Examples not found"
find build -name "*-capture_callback" -exec cp {} package/examples/ \; || echo "Examples not found"
find build -name "*-example_with_glfw" -exec cp {} package/examples/ \; || echo "Examples not found"
- name: Copy libraries (Linux)
if: matrix.os == 'ubuntu-latest' && matrix.arch != 'arm64'
shell: bash
run: |
if [ "${{ matrix.shared }}" = "true" ]; then
# Copy shared library
cp build/libccap.so package/lib/ || echo "Shared library not found"
else
# Copy static library
cp build/libccap.a package/lib/ || echo "Static library not found"
fi
# Copy example programs
find build -name "0-print_camera" -exec cp {} package/examples/ \; || echo "Examples not found"
find build -name "1-minimal_example" -exec cp {} package/examples/ \; || echo "Examples not found"
find build -name "2-capture_grab" -exec cp {} package/examples/ \; || echo "Examples not found"
find build -name "3-capture_callback" -exec cp {} package/examples/ \; || echo "Examples not found"
find build -name "4-example_with_glfw" -exec cp {} package/examples/ \; || echo "Examples not found"
- name: Copy libraries (Linux ARM64)
if: matrix.os == 'ubuntu-latest' && matrix.arch == 'arm64'
shell: bash
run: |
if [ "${{ matrix.shared }}" = "true" ]; then
# Copy shared library
cp build/libccap.so package/lib/ || echo "Shared library not found"
else
# Copy static library
cp build/libccap.a package/lib/ || echo "Static library not found"
fi
# Copy example programs (ARM64 cross-compiled)
find build -name "0-print_camera" -exec cp {} package/examples/ \; || echo "Examples not found"
find build -name "1-minimal_example" -exec cp {} package/examples/ \; || echo "Examples not found"
find build -name "2-capture_grab" -exec cp {} package/examples/ \; || echo "Examples not found"
find build -name "3-capture_callback" -exec cp {} package/examples/ \; || echo "Examples not found"
find build -name "4-example_with_glfw" -exec cp {} package/examples/ \; || echo "Examples not found"
- name: Copy headers and other files
shell: bash
run: |
# Copy header files
cp -r include/* package/include/
# Copy CMake configuration files
cp build/ccap*.cmake package/cmake/ || echo "CMake config files not found"
cp build/ccap.pc package/ || echo "pkg-config file not found"
# Copy documentation
cp README.md package/ || echo "README not found"
cp README.zh-CN.md package/ || echo "Chinese README not found"
cp LICENSE package/ || echo "LICENSE not found"
cp BUILD_AND_INSTALL.md package/ || echo "Build instructions not found"
cp PACKAGE_USAGE.md package/ || echo "Package usage guide not found"
# Copy example source files
cp examples/desktop/*.cpp package/examples/ || echo "Example source files not found"
- name: Verify package contents (Windows)
if: matrix.os == 'windows-latest'
shell: bash
run: |
echo "=== Package contents ==="
find package -name "*.lib" -o -name "*.dll" -o -name "*.exe" | sort
echo "=== lib directory ==="
ls -la package/lib/ || echo "lib directory not found"
- name: Create archive
shell: bash
run: |
cd package
if [ "${{ matrix.os }}" = "windows-latest" ]; then
# Windows: Create ZIP file
7z a ../${{ matrix.artifact_name }}.zip ./*
else
# macOS and Linux: Create tar.gz file
tar -czf ../${{ matrix.artifact_name }}.tar.gz .
fi
cd ..
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact_name }}
path: ${{ matrix.artifact_name }}.*
retention-days: 5
build-cli:
name: Build CLI Tool
needs: test
strategy:
fail-fast: false
matrix:
include:
# macOS CLI (normal build, CCAP_BUILD_CLI_STANDALONE not used)
- os: macos-latest
name: macOS CLI
artifact_name: ccap-cli-macos-universal
build_type: Release
# Windows CLI (static MSVC runtime, no DLL dependencies)
- os: windows-latest
name: Windows CLI
artifact_name: ccap-cli-msvc-x86_64
build_type: Release
standalone: true
# Linux x86_64 CLI (glibc version built in Docker ubuntu:20.04)
- os: ubuntu-latest
name: Linux x86_64 CLI (glibc)
artifact_name: ccap-cli-linux-x86_64-gnu
build_type: Release
arch: x86_64
docker_ubuntu20: true
# Linux ARM64 CLI (glibc version built in Docker ubuntu:20.04)
- os: ubuntu-latest
name: Linux ARM64 CLI (glibc)
artifact_name: ccap-cli-linux-arm64-gnu
build_type: Release
arch: aarch64
docker_ubuntu20: true
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up QEMU for multi-arch (Linux glibc)
if: matrix.docker_ubuntu20 == true
uses: docker/setup-qemu-action@v3
with:
platforms: arm64
- name: Setup Visual Studio (Windows)
if: matrix.os == 'windows-latest'
uses: microsoft/setup-msbuild@v1.1
- name: Build CLI with Docker Ubuntu 20.04 (Linux glibc)
if: matrix.docker_ubuntu20 == true
shell: bash
run: |
# Determine platform
if [ "${{ matrix.arch }}" = "aarch64" ]; then
DOCKER_PLATFORM="linux/arm64"
CMAKE_ARCH="aarch64"
else
DOCKER_PLATFORM="linux/amd64"
CMAKE_ARCH="x86_64"
fi
echo "Building CLI for ${{ matrix.arch }} in Ubuntu 20.04 Docker container"
echo "Platform: $DOCKER_PLATFORM"
# Create output directory
mkdir -p cli-package
# Build in Ubuntu 20.04 container (GLIBC 2.31)
docker run --rm --platform=$DOCKER_PLATFORM \
-v "$(pwd):/workspace" \
-w /workspace \
ubuntu:20.04 \
bash -c "
set -e
export DEBIAN_FRONTEND=noninteractive
echo '=== Installing build dependencies ==='
apt-get update
apt-get install -y cmake build-essential g++ pkg-config file \
libglfw3-dev \
libwayland-dev libxkbcommon-dev wayland-protocols \
libx11-dev libxrandr-dev libxinerama-dev \
libxcursor-dev libxi-dev libxext-dev
echo '=== Configuring CMake ==='
cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
-DCCAP_BUILD_EXAMPLES=OFF \
-DCCAP_BUILD_TESTS=OFF \
-DCCAP_BUILD_SHARED=OFF \
-DCCAP_BUILD_CLI_STANDALONE=ON \
-DCCAP_CLI_WITH_GLFW=ON
echo '=== Building CLI ==='
cmake --build build --config ${{ matrix.build_type }} --target ccap-cli --parallel \$(nproc)
echo '=== Verifying build ==='
if [ ! -f build/ccap ]; then
echo 'ERROR: CLI executable not found'
exit 1
fi
# Copy CLI executable
cp build/ccap cli-package/ccap
chmod +x cli-package/ccap
echo '=== Build information ==='
file cli-package/ccap
ldd cli-package/ccap || true
echo '=== glibc version requirements ==='
objdump -T cli-package/ccap | grep GLIBC_ | sed 's/.*GLIBC_/GLIBC_/g' | sort -u
echo '=== CLI Version ==='
cli-package/ccap --version || true
"
- name: Configure CMake (macOS/Windows)
if: matrix.os == 'macos-latest' || matrix.os == 'windows-latest'
shell: bash
run: |
# Set standalone flag based on matrix configuration
STANDALONE_FLAG=""
if [ "${{ matrix.standalone }}" = "true" ]; then
STANDALONE_FLAG="-DCCAP_BUILD_CLI_STANDALONE=ON"
fi
if [ "${{ matrix.os }}" = "windows-latest" ]; then
# Windows: Use multi-config generator (Visual Studio)
cmake -B build -G "Visual Studio 17 2022" -A x64 \
-DCCAP_BUILD_EXAMPLES=OFF \
-DCCAP_BUILD_TESTS=OFF \
-DCCAP_BUILD_SHARED=OFF \
-DCCAP_BUILD_CLI=ON \
-DCCAP_CLI_WITH_GLFW=ON \
$STANDALONE_FLAG
else
# macOS: Use single-config generator with universal binary
# Note: CCAP_BUILD_CLI_STANDALONE not used on macOS
cmake -B build -DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' \
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
-DCCAP_BUILD_EXAMPLES=OFF \
-DCCAP_BUILD_TESTS=OFF \
-DCCAP_BUILD_SHARED=OFF \
-DCCAP_BUILD_CLI=ON \
-DCCAP_CLI_WITH_GLFW=ON
fi
- name: Build CLI (macOS/Windows)
if: matrix.os == 'macos-latest' || matrix.os == 'windows-latest'
shell: bash
run: |
if [ "${{ matrix.os }}" = "windows-latest" ]; then
# Windows: Build Release version only
cmake --build build --config Release --target ccap-cli --parallel
else
# macOS: Build specified version
cmake --build build --config ${{ matrix.build_type }} --target ccap-cli --parallel
fi
- name: Verify CLI build information
shell: bash
run: |
echo "=== Verifying CLI build configuration ==="
if [ "${{ matrix.os }}" = "windows-latest" ]; then
CLI_PATH="build/Release/ccap.exe"
elif [ "${{ matrix.docker_ubuntu20 }}" = "true" ]; then
# Linux glibc builds are in cli-package
CLI_PATH="cli-package/ccap"
else
CLI_PATH="build/ccap"
fi
# Check if CLI executable exists
if [ ! -f "$CLI_PATH" ]; then
echo "ERROR: CLI executable not found at $CLI_PATH"
exit 1
fi
echo "✓ CLI executable found: $CLI_PATH"
ls -lh "$CLI_PATH"
# Check if this is an ARM64 cross-compilation (can't execute directly on x86_64 runner)
if [ "${{ matrix.arch }}" = "aarch64" ] && [ "${{ matrix.docker_ubuntu20 }}" = "true" ]; then
echo ""
echo "=== ARM64 Cross-Compilation Build ==="
echo "⚠ Cannot execute ARM64 binary directly on x86_64 runner"
echo "📦 Setting up containerized verification with QEMU..."
# Verify file type
echo ""
echo "=== File Information ==="
file "$CLI_PATH"
# Run in ARM64 container with QEMU
echo ""
echo "=== Testing in ARM64 Container (via QEMU) ==="
# Create temporary test directory
mkdir -p /tmp/arm64-test
cp "$CLI_PATH" /tmp/arm64-test/ccap
chmod +x /tmp/arm64-test/ccap
# Pull ARM64 image explicitly first to avoid architecture mismatch
docker pull --platform=linux/arm64 ubuntu:24.04
# Run verification in ARM64 container
docker run --rm --platform=linux/arm64 \
-v "/tmp/arm64-test:/test:ro" \
-w /test \
ubuntu:24.04 \
sh -c "
echo '=== Container Environment ==='
uname -m
cat /etc/os-release | head -3
echo ''
echo '=== CLI Version Test ==='
./ccap --version || exit 1
echo ''
echo '=== CLI Help Test ==='
./ccap --help > /dev/null 2>&1 || exit 1
echo '✓ --help command works'
echo ''
echo '=== Feature Detection ==='
VERSION_OUTPUT=\$(./ccap --version)
# Check standalone mode
if echo \"\$VERSION_OUTPUT\" | grep -q 'Runtime Linking: Static'; then
echo '✓ Standalone mode: ENABLED (Static runtime)'
else
echo 'ℹ Standalone mode not explicitly reported'
fi
# Check stb_image
if echo \"\$VERSION_OUTPUT\" | grep -qi 'stb_image'; then
echo '✓ stb_image_write: ENABLED'
else
echo 'ℹ stb_image_write not mentioned in version output'
fi
# Check GLFW (should be enabled for glibc CLI)
if echo \"\$VERSION_OUTPUT\" | grep -qi 'glfw\|preview'; then
echo '✓ GLFW: ENABLED (as expected)'
else
echo '⚠ WARNING: GLFW not detected (should be enabled)'
fi
echo ''
echo '✓ All ARM64 container tests passed!'
"
echo ""
echo "✓ ARM64 binary verified successfully in container"
exit 0
fi
# For Linux glibc builds, version check was already done in Docker
if [ "${{ matrix.docker_ubuntu20 }}" = "true" ]; then
echo ""
echo "✓ Linux glibc build verification completed in Docker container"
exit 0
fi
# Parse version output to verify features
VERSION_OUTPUT=$($CLI_PATH --version)
# Check CCAP_BUILD_CLI_STANDALONE
echo "=== Verifying Standalone Mode ==="
if [ "${{ matrix.standalone }}" = "true" ]; then
if echo "$VERSION_OUTPUT" | grep -q "Runtime Linking: Static"; then
echo "✓ Standalone mode: ENABLED (Static runtime)"
else
echo "⚠ WARNING: Standalone mode expected but not detected in version output"
echo "Version output:"
echo "$VERSION_OUTPUT"
fi
else
echo "ℹ Standalone mode not required for this platform (${{ matrix.os }})"
fi
# Check stb_image_write support
echo ""
echo "=== Verifying stb_image_write (JPG/PNG support) ==="
if echo "$VERSION_OUTPUT" | grep -qi "stb_image"; then
echo "✓ stb_image_write: ENABLED"
else
echo "⚠ WARNING: stb_image_write not mentioned in version output"
fi
# Check GLFW support
echo ""
echo "=== Verifying GLFW (Preview support) ==="
if echo "$VERSION_OUTPUT" | grep -qi "glfw\|preview"; then
echo "✓ GLFW: ENABLED (as expected)"
else
echo "⚠ WARNING: GLFW not detected (should be enabled)"
fi
# Platform-specific dependency checks
if [ "${{ matrix.os }}" = "windows-latest" ]; then
echo ""
echo "=== Windows Dependency Check ==="
echo "File info:"
file "$CLI_PATH" || echo "file command not available"
echo "✓ Windows static MSVC runtime expected"
elif [ "${{ matrix.docker_ubuntu20 }}" = "true" ]; then
echo ""
echo "=== Linux Dependency Check ==="
echo "File info:"
file "$CLI_PATH"
echo ""
echo "Dynamic library dependencies:"
ldd "$CLI_PATH"
echo ""
# Check for static libstdc++/libgcc (glibc builds are standalone)
if true; then
if ldd "$CLI_PATH" | grep -q "libstdc++"; then
echo "⚠ WARNING: libstdc++ is dynamically linked (static linking may not be available)"
else
echo "✓ libstdc++ is statically linked"
fi
if ldd "$CLI_PATH" | grep -q "libgcc_s"; then
echo "⚠ WARNING: libgcc_s is dynamically linked (static linking may not be available)"
else
echo "✓ libgcc_s is statically linked"
fi
fi
# Detect glibc version
echo ""
echo "glibc version requirement:"
objdump -T "$CLI_PATH" | grep GLIBC_ | sed 's/.*GLIBC_/GLIBC_/g' | sort -u || echo "Cannot detect glibc version"
elif [ "${{ matrix.os }}" = "macos-latest" ]; then
echo ""
echo "=== macOS Dependency Check ==="
echo "File info:"
file "$CLI_PATH"
echo ""
echo "Architecture info:"
lipo -info "$CLI_PATH"
echo ""
echo "Dynamic library dependencies:"
otool -L "$CLI_PATH"
echo ""
echo "✓ macOS uses static libc++ by default"
fi
# Test basic functionality
echo ""
echo "=== Testing Basic Functionality ==="
if $CLI_PATH --help > /dev/null 2>&1; then
echo "✓ --help command works"
else
echo "✗ ERROR: --help command failed"
exit 1
fi
if $CLI_PATH --version > /dev/null 2>&1; then
echo "✓ --version command works"
else
echo "✗ ERROR: --version command failed"
exit 1
fi
echo ""
echo "=== Verification Complete ==="
- name: Verify CLI in clean container (Linux glibc)
if: matrix.docker_ubuntu20 == true
shell: bash
run: |
echo "=== Verifying CLI in Clean Container Environment ==="
echo "This test ensures the CLI can run without any development dependencies"
# Create a temporary directory for the test
mkdir -p /tmp/cli-test
cp cli-package/ccap /tmp/cli-test/ccap
chmod +x /tmp/cli-test/ccap
# Determine architecture and platform
if [ "${{ matrix.arch }}" = "aarch64" ]; then
DOCKER_PLATFORM="linux/arm64"
else
DOCKER_PLATFORM="linux/amd64"
fi
echo ""
echo "Running CLI in minimal Debian container (no build tools, no X11, no GPU)..."
echo "Platform: $DOCKER_PLATFORM"
# Use debian:stable-slim as a minimal glibc environment
# This container has only basic glibc runtime, no development tools
docker run --rm --platform=$DOCKER_PLATFORM \
-v "/tmp/cli-test:/app:ro" \
-w /app \
debian:stable-slim \
sh -c "
set -e
echo '=== Container Environment ==='
cat /etc/os-release | head -3
echo ''
echo '=== Testing CLI Execution ==='
# Test --version
echo 'Testing --version...'
if ./ccap --version; then
echo '✓ --version: PASSED'
else
echo '✗ --version: FAILED'
exit 1
fi
echo ''
# Test --help
echo 'Testing --help...'
if ./ccap --help > /dev/null 2>&1; then
echo '✓ --help: PASSED'
else
echo '✗ --help: FAILED'
exit 1
fi
echo ''
# Test --list-devices (should work even without cameras)
echo 'Testing --list-devices...'
if ./ccap --list-devices; then
echo '✓ --list-devices: PASSED'
else
echo '✗ --list-devices: FAILED (exit code non-zero, but may be OK if no devices)'
fi
echo ''
echo '=== All Clean Container Tests PASSED ==='
"
echo ""
echo "✓ CLI successfully runs in clean container without development dependencies"
# Cleanup
rm -rf /tmp/cli-test
- name: Prepare CLI package directory
shell: bash
run: |
mkdir -p cli-package
- name: Copy CLI executable (Windows)
if: matrix.os == 'windows-latest'
shell: bash
run: |
# Copy CLI executable
cp build/Release/ccap.exe cli-package/ccap.exe || echo "CLI executable not found"
# Copy required DLLs if any
# Note: Static build should not need additional DLLs
- name: Copy CLI executable (macOS)
if: matrix.os == 'macos-latest'
shell: bash
run: |
# Copy CLI executable
cp build/ccap cli-package/ccap || echo "CLI executable not found"
# Verify universal binary
if [ -f "cli-package/ccap" ]; then
file cli-package/ccap
lipo -info cli-package/ccap
fi
- name: Copy CLI executable (Linux glibc - already in cli-package)
if: matrix.docker_ubuntu20 == true
shell: bash
run: |
# Verify CLI executable (already copied by Docker build)
if [ -f "cli-package/ccap" ]; then
echo "✓ CLI executable found in cli-package/"
file cli-package/ccap
else
echo "ERROR: CLI executable not found"
exit 1
fi
- name: Copy documentation
shell: bash
run: |
# Copy README files
cp README.md cli-package/ || echo "README not found"
cp README.zh-CN.md cli-package/ || echo "Chinese README not found"
cp LICENSE cli-package/ || echo "LICENSE not found"
# Create platform-specific usage guide for CLI
if [ "${{ matrix.os }}" = "macos-latest" ]; then
# macOS-specific USAGE.md with Gatekeeper instructions
cat > cli-package/USAGE.md << 'EOF'
# ccap CLI Tool Usage (macOS)
## 🚀 Quick Start
### First-time Setup (macOS Security)
macOS Gatekeeper will block this executable because it's not signed by Apple.
You have **two options**:
**Option 1: Use the convenience script (recommended)**
```bash
# Run ccap through the wrapper script (handles security automatically)
./run_ccap.sh --list-devices
./run_ccap.sh --help
```
**Option 2: Remove quarantine attribute manually**
```bash
# One-time setup - remove the quarantine attribute
xattr -d com.apple.quarantine ccap
chmod +x ccap
# Now you can run ccap directly
./ccap --list-devices
```
**Option 3: Use System Preferences**
1. Try to run `./ccap`
2. macOS will show a security warning
3. Go to **System Preferences** → **Security & Privacy** → **General**
4. Click **"Open Anyway"** next to the blocked message
5. Try running `./ccap` again and click **"Open"**
---
## Usage Examples
```bash
# List all available cameras
./ccap --list-devices
# Capture a single frame (saves as output.bmp by default)
./ccap
# Capture with specific device
./ccap --device 0
# Capture with specific resolution
./ccap --width 1920 --height 1080
# Capture with specific pixel format
./ccap --format YUYV
# Capture with internal format (camera native format)
./ccap --internal-format MJPEG
# Capture multiple frames
./ccap --count 10
# Save to specific file
./ccap --output my-capture.bmp
# Preview window (with GLFW support)
./ccap --preview
```
## Available Options
Run `./ccap --help` for complete list of options.
## 🔧 Troubleshooting
### "ccap cannot be opened because the developer cannot be verified"
This is macOS Gatekeeper security feature. See the **First-time Setup** section above for solutions.
### Check if quarantine attribute is set
```bash
xattr -l ccap
# If you see "com.apple.quarantine", it's quarantined
```
### Remove quarantine from all files in directory
```bash
xattr -dr com.apple.quarantine .
```
## System Requirements
- **macOS**: 10.13 or higher
- Universal Binary (supports both Intel and Apple Silicon)
## Notes
- This CLI tool is statically linked and has no external dependencies
- BMP format is the only supported output format
- For more advanced usage, please refer to the ccap library documentation
- The `run_ccap.sh` script automatically handles macOS security on first run
EOF
else
# Generic USAGE.md for Windows and Linux
cat > cli-package/USAGE.md << 'EOF'
# ccap CLI Tool Usage