-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathinstall-xcode-scripts.sh
More file actions
executable file
·582 lines (459 loc) · 17.5 KB
/
install-xcode-scripts.sh
File metadata and controls
executable file
·582 lines (459 loc) · 17.5 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
#!/bin/bash
# Installer for lightweight Xcode build & log capture scripts
# Based on Igor Makarov's approach: https://gist.github.com/igor-makarov/e46c7827493016765d6431b6bd1d2394
#
# Usage:
# bash install-xcode-scripts.sh # Install in current directory
# bash install-xcode-scripts.sh /path/to/project # Install in target directory
#
# This script will:
# - Create .claude/scripts/ directory
# - Create ./build/logs/ and ./build/xcodebuild/ directories
# - Install capture-logs, stop-logs, and xcodebuild scripts
# - Copy building-with-xcode.md documentation
# - Create/update CLAUDE.md and AGENTS.md if they exist
# - Make all scripts executable
set -e
echo "🔧 Installing Xcode build & log capture scripts..."
echo ""
# Color codes
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Get target directory - use argument or current directory
TARGET_DIR="${1:-.}"
# Validate target directory exists
if [ ! -d "$TARGET_DIR" ]; then
echo "❌ Error: Target directory does not exist: $TARGET_DIR"
exit 1
fi
# Convert to absolute path
TARGET_DIR="$(cd "$TARGET_DIR" && pwd)"
echo "📂 Installing to: $TARGET_DIR"
echo ""
echo "📂 Creating directories..."
mkdir -p "${TARGET_DIR}/.claude/scripts"
mkdir -p "${TARGET_DIR}/build/logs"
mkdir -p "${TARGET_DIR}/build/xcodebuild"
echo " ✅ Directories created"
echo ""
echo "📝 Creating capture-logs script..."
cat > "${TARGET_DIR}/.claude/scripts/capture-logs" << 'CAPTURE_LOGS_SCRIPT'
#!/bin/bash
# Captures visionOS/iOS simulator logs to timestamped file
# Usage: ./claude/scripts/capture-logs <subsystem-or-bundle-id> [simulator-id]
# Example: ./claude/scripts/capture-logs com.yourapp.vision
# Example: ./claude/scripts/capture-logs com.yourapp.vision booted
set -e
if [ $# -eq 0 ]; then
echo "❌ Usage: ./claude/scripts/capture-logs <subsystem-or-bundle-id> [simulator-id]"
echo " Example: ./claude/scripts/capture-logs com.yourapp.vision"
echo " Example: ./claude/scripts/capture-logs com.yourapp.vision booted"
exit 1
fi
SUBSYSTEM=$1
SIM_ID=${2:-booted}
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
OUTPUT="./build/logs/logs-${TIMESTAMP}.txt"
# Ensure output directory exists
mkdir -p ./build/logs
echo "📝 Starting log capture..."
echo " Subsystem: $SUBSYSTEM"
echo " Simulator: $SIM_ID"
echo " Output: $OUTPUT"
echo ""
# Start capturing logs filtered by subsystem in background
# --level debug ensures Debug-level logs are captured (not just Info and Error)
# --style compact provides readable formatting
xcrun simctl spawn "$SIM_ID" log stream --level debug --style compact --predicate "subsystem == '${SUBSYSTEM}'" > "$OUTPUT" 2>&1 &
PID=$!
# Store the PID so we can stop it later
echo "$PID" > ./build/logs/.last-capture-pid
echo "✅ Log capture started"
echo "🔢 Process ID: $PID"
echo "🛑 To stop capturing, run: ./claude/scripts/stop-logs"
echo "📂 Logs will be saved to: $OUTPUT"
CAPTURE_LOGS_SCRIPT
chmod +x "${TARGET_DIR}/.claude/scripts/capture-logs"
echo " ✅ capture-logs installed"
echo ""
echo "📝 Creating stop-logs script..."
cat > "${TARGET_DIR}/.claude/scripts/stop-logs" << 'STOP_LOGS_SCRIPT'
#!/bin/bash
# Stops the active log capture process
# Usage: ./claude/scripts/stop-logs
PID_FILE="./build/logs/.last-capture-pid"
if [ ! -f "$PID_FILE" ]; then
echo "❌ No active log capture found"
exit 1
fi
PID=$(cat "$PID_FILE")
if ps -p "$PID" > /dev/null 2>&1; then
kill "$PID"
rm "$PID_FILE"
echo "✅ Stopped log capture (PID $PID)"
else
echo "⚠️ Process $PID is not running"
rm "$PID_FILE"
exit 1
fi
STOP_LOGS_SCRIPT
chmod +x "${TARGET_DIR}/.claude/scripts/stop-logs"
echo " ✅ stop-logs installed"
echo ""
echo "📝 Creating xcodebuild wrapper script..."
cat > "${TARGET_DIR}/.claude/scripts/xcodebuild" << 'XCODEBUILD_SCRIPT'
#!/bin/bash
# Wrapper around xcodebuild that captures output to timestamped files
# This follows Igor Makarov's approach: https://gist.github.com/igor-makarov/e46c7827493016765d6431b6bd1d2394
#
# Usage: ./claude/scripts/xcodebuild <standard xcodebuild args>
# Examples:
# ./claude/scripts/xcodebuild -workspace MyApp.xcworkspace -scheme MyApp build
# ./claude/scripts/xcodebuild -workspace MyApp.xcworkspace -scheme MyApp test -destination 'generic/platform=visionOS'
set -e
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
OUTPUT_DIR="./build/xcodebuild"
OUTPUT_FILE="$OUTPUT_DIR/build-${TIMESTAMP}.txt"
# Ensure output directory exists
mkdir -p "$OUTPUT_DIR"
echo "🔨 Running xcodebuild..."
echo "📝 Output will be saved to: $OUTPUT_FILE"
echo ""
# Run xcodebuild and capture output
if xcodebuild "$@" > "$OUTPUT_FILE" 2>&1; then
EXIT_CODE=0
else
EXIT_CODE=$?
fi
# Get file size
FILE_SIZE=$(du -h "$OUTPUT_FILE" | cut -f1)
echo ""
echo "✅ xcodebuild completed"
echo "📊 Exit code: $EXIT_CODE"
echo "📦 Output size: $FILE_SIZE"
echo "📂 Output file: $OUTPUT_FILE"
echo ""
echo "💡 Tip: Use grep to search the output, e.g.:"
echo " grep -i error '$OUTPUT_FILE'"
echo " grep -i warning '$OUTPUT_FILE'"
exit "$EXIT_CODE"
XCODEBUILD_SCRIPT
chmod +x "${TARGET_DIR}/.claude/scripts/xcodebuild"
echo " ✅ xcodebuild installed"
echo ""
echo "📝 Creating documentation..."
cat > "${TARGET_DIR}/.claude/building-with-xcode.md" << 'DOCUMENTATION'
# Building with Xcode - Claude Instructions
This document explains how to use the lightweight build and log capture wrapper scripts instead of the heavy XcodeBuildMCP server.
## Philosophy
This approach replaces XcodeBuildMCP's 84+ tools with simple bash wrappers that:
- ✅ Just work reliably
- ✅ Capture output to files (token-efficient)
- ✅ Follow Igor Makarov's proven pattern
- ✅ Let you search/grep results instead of loading everything
## Build Wrapper: `./claude/scripts/xcodebuild`
### Purpose
Wraps standard `xcodebuild` commands and captures output to timestamped files.
### Usage
```bash
./claude/scripts/xcodebuild -workspace XXX.xcworkspace -scheme YYY <action> [options]
```
### Examples
**Build for visionOS Simulator:**
```bash
./claude/scripts/xcodebuild -workspace MyApp.xcworkspace -scheme MyApp build -destination 'generic/platform=visionOS Simulator'
```
**Run unit tests:**
```bash
./claude/scripts/xcodebuild -workspace MyApp.xcworkspace -scheme MyApp test -destination 'generic/platform=visionOS Simulator'
```
**Build for device:**
```bash
./claude/scripts/xcodebuild -workspace MyApp.xcworkspace -scheme MyApp build -destination generic/platform=visionOS
```
**Archive for distribution:**
```bash
./claude/scripts/xcodebuild -workspace MyApp.xcworkspace -scheme MyApp archive -archivePath ./build/MyApp.xcarchive
```
### Output
- ✅ Builds to: `./build/xcodebuild/build-YYYYMMDD-HHMMSS.txt`
- ✅ Reports: Exit code, file size
- ✅ Searchable via grep
### Important Notes
- ❌ **NEVER use `-clean`** - Not needed, slows down builds
- ✅ Use existing build cache between builds
- ✅ Search output files with grep instead of loading entire contents
## Log Capture: `./claude/scripts/capture-logs` and `./claude/scripts/stop-logs`
### Purpose
Captures app console output (print statements, NSLog, os_log) to timestamped files.
### Typical Workflow
**Step 1: Start log capture**
```bash
./claude/scripts/capture-logs com.example.myapp
```
**Step 2: Launch your app in Xcode** (Cmd+R)
- Your app runs normally
- Logs are captured in background
**Step 3: Interact with the app**
- Reproduce the issue
- Trigger the feature you're testing
**Step 4: Stop log capture**
```bash
./claude/scripts/stop-logs
```
**Step 5: Search the logs**
```bash
grep -i error ./build/logs/logs-*.txt
grep -i "ViewModel" ./build/logs/logs-*.txt
```
### Subsystem Filtering
The `capture-logs` script filters by subsystem/bundle ID to reduce noise.
**Examples:**
```bash
# Capture logs from your app's main subsystem
./claude/scripts/capture-logs com.mycompany.myapp
# Capture logs from a specific subsystem
./claude/scripts/capture-logs com.mycompany.myapp.CoreData
# If you're not sure about the subsystem, use a prefix:
# (Note: simctl's predicate syntax might require exact matches)
./claude/scripts/capture-logs com.example
```
### Output
- ✅ Logs to: `./build/logs/logs-YYYYMMDD-HHMMSS.txt`
- ✅ Stores PID for cleanup
- ✅ Independent of xcodebuild builds
### Stopping Logs
Always stop logs after testing:
```bash
./claude/scripts/stop-logs
```
This kills the background log capture process.
## File Organization
```
./build/
├── xcodebuild/ # Build outputs
│ └── build-*.txt # One per build
└── logs/ # App logs
├── logs-*.txt # One per capture session
└── .last-capture-pid # PID of active capture (temporary)
```
## Search Patterns
### Find Build Errors
```bash
grep -i "error:" ./build/xcodebuild/build-*.txt
```
### Find Warnings
```bash
grep -i "warning:" ./build/xcodebuild/build-*.txt
```
### Find Test Failures
```bash
grep -i "failed" ./build/xcodebuild/build-*.txt
```
### Search App Logs
```bash
grep "ViewController" ./build/logs/logs-*.txt
grep -i "crash" ./build/logs/logs-*.txt
grep "ERROR" ./build/logs/logs-*.txt
```
### Count Occurrences
```bash
grep -c "MyClass" ./build/logs/logs-*.txt
```
### Show Context
```bash
grep -C 3 "error" ./build/logs/logs-*.txt # 3 lines before/after
```
## Workflow Examples
### Debugging App Crash
1. Start logs: `./claude/scripts/capture-logs com.myapp.vision`
2. Launch app: Cmd+R in Xcode
3. Reproduce crash
4. Stop logs: `./claude/scripts/stop-logs`
5. Search: `grep -i "crash\|exception\|error" ./build/logs/logs-*.txt`
### Debugging Build Failures
1. Build: `./claude/scripts/xcodebuild -workspace MyApp.xcworkspace -scheme MyApp build -destination 'generic/platform=visionOS Simulator'`
2. Search: `grep -i "error" ./build/xcodebuild/build-*.txt`
3. Show context: `grep -C 5 "error" ./build/xcodebuild/build-*.txt`
### Performance Profiling
1. Start logs: `./claude/scripts/capture-logs com.myapp.vision`
2. Launch app: Cmd+R
3. Run performance test
4. Stop logs: `./claude/scripts/stop-logs`
5. Search for performance markers: `grep "duration\|elapsed\|ms" ./build/logs/logs-*.txt`
## Advantages Over XcodeBuildMCP
| Aspect | XcodeBuildMCP | These Scripts |
|--------|---------------|---------------|
| Complexity | 84+ tools, TypeScript server | 3 bash scripts, ~100 lines total |
| Reliability | ❌ Often fails | ✅ Just wraps Apple's tools |
| Token Efficiency | ❌ Large responses | ✅ File-based, search what you need |
| Setup Time | ⏱️ Complex configuration | ⚡ Works immediately |
| Maintenance | 🔧 Requires debugging | 🔒 Stable, no dependencies |
| Features | 🌟 Many tools | 📍 Focused, what you need |
## Key Principles
1. **File-based outputs** - Save to timestamped files, not memory
2. **Search, don't load** - Use grep/head/tail to find what you need
3. **Simple tools** - Bash wrappers, not complex protocols
4. **Independent** - Log capture and builds don't interfere
5. **Reliable** - Built on Apple's battle-tested tools
## Troubleshooting
### Log capture fails with "Invalid simulator"
- Check simulator is running: `xcrun simctl list`
- Use correct simulator ID or "booted" for active simulator
- Example: `./claude/scripts/capture-logs com.myapp.vision booted`
### Logs not appearing
- Verify subsystem name matches your app's logging setup
- Check that your app is actually writing logs with that subsystem
- Try: `./claude/scripts/capture-logs com.myapp.vision` (may capture all)
### xcodebuild wrapper doesn't find workspace
- Verify path: `ls -la XXX.xcworkspace`
- Must be relative to where you run the script
- Example: `./claude/scripts/xcodebuild -workspace ./MyApp.xcworkspace ...`
### Can't find grep results
- Check file exists: `ls -la ./build/logs/ ./build/xcodebuild/`
- Try without wildcards: `grep error ./build/logs/logs-20251020-*.txt`
- Use `-r` for recursive: `grep -r error ./build/`
## Integration with Claude Code
When working with Claude Code:
1. **Ask Claude to run builds:** "Build the visionOS app"
- Claude will call: `./claude/scripts/xcodebuild ...`
- Results saved to file
- Claude reads file with grep
2. **Ask Claude to capture logs:** "Capture logs from my app"
- Claude will call: `./claude/scripts/capture-logs com.yourapp`
- Launch app manually in Xcode
- Ask Claude to read logs after stopping
3. **Ask Claude to analyze:** "Find errors in the build"
- Claude greps the build output file
- Shows only relevant errors
- No token waste from full output
## Next Steps
- Review scripts in `./.claude/scripts/`
- Start with: `./claude/scripts/xcodebuild -h` (for xcodebuild help)
- Try a build: `./claude/scripts/xcodebuild -workspace MyApp.xcworkspace -scheme MyApp build`
- Test log capture with your app
DOCUMENTATION
echo " ✅ building-with-xcode.md created"
echo ""
echo "📝 Updating project documentation..."
# Create or update CLAUDE.md
CLAUDE_CONTENT="## Xcode Build & Log Capture Scripts
This project uses lightweight bash wrappers instead of XcodeBuildMCP:
### Scripts
- **\`./claude/scripts/xcodebuild\`** - Wraps xcodebuild, captures output to \`./build/xcodebuild/build-*.txt\`
- **\`./claude/scripts/capture-logs\`** - Captures app logs to \`./build/logs/logs-*.txt\`
- **\`./claude/scripts/stop-logs\`** - Stops active log capture
### Usage
\`\`\`bash
# Build
./claude/scripts/xcodebuild -workspace MyApp.xcworkspace -scheme MyApp build
# Capture logs
./claude/scripts/capture-logs com.yourapp.vision
./claude/scripts/stop-logs
# Search results
grep -i error ./build/xcodebuild/build-*.txt
grep \"ViewController\" ./build/logs/logs-*.txt
\`\`\`
For details, see \`./\.claude/building-with-xcode.md\`"
if [ -f "${TARGET_DIR}/CLAUDE.md" ]; then
if ! grep -q "xcode-cli-tools\|Xcode Build & Log Capture" "${TARGET_DIR}/CLAUDE.md"; then
echo "" >> "${TARGET_DIR}/CLAUDE.md"
echo "$CLAUDE_CONTENT" >> "${TARGET_DIR}/CLAUDE.md"
echo " ✅ CLAUDE.md updated"
else
echo " ℹ️ CLAUDE.md already contains Xcode tools info"
fi
else
echo "# Project Configuration
$CLAUDE_CONTENT" > "${TARGET_DIR}/CLAUDE.md"
echo " ✅ CLAUDE.md created"
fi
# Create or update AGENTS.md
AGENTS_CONTENT="## Xcode CLI Tools Integration
Use these lightweight bash wrappers for all Xcode operations.
### When to Use Each Script
| Task | Script | Output | Approach |
|------|--------|--------|----------|
| Build app | \`./claude/scripts/xcodebuild [args]\` | \`./build/xcodebuild/build-*.txt\` | Run, grep for errors |
| Capture logs | \`./claude/scripts/capture-logs <bundle-id>\` | \`./build/logs/logs-*.txt\` | Start, user launches app, stop, grep |
| Stop logs | \`./claude/scripts/stop-logs\` | Console | Stop active capture |
### Key Rules
1. **Never use \`-clean\`** - Waste time, use build cache
2. **Search output, don't load it** - Use \`grep -i error ./build/xcodebuild/build-*.txt\`
3. **Specify workspace/scheme** - Required for every build command
4. **Subsystem filtering** - Use exact bundle ID in capture-logs
5. **File-based workflow** - All outputs saved timestamped, search via grep
### Examples
**Build for visionOS Simulator:**
\`\`\`bash
./claude/scripts/xcodebuild -workspace MyApp.xcworkspace -scheme MyApp build -destination 'generic/platform=visionOS Simulator'
\`\`\`
**Debug log capture:**
\`\`\`bash
# User: \"Capture logs from my app while I test\"
./claude/scripts/capture-logs com.mycompany.myapp
# User launches app in Xcode
# User triggers issue
# User: \"Stop capturing logs\"
./claude/scripts/stop-logs
# Search: grep -i error ./build/logs/logs-*.txt
\`\`\`
**Find build errors:**
\`\`\`bash
grep -i \"error:\" ./build/xcodebuild/build-*.txt
\`\`\`
### Token Efficiency
- Output files are timestamped and searched via grep
- Never load full build outputs into context
- Use \`grep -C 3 \"error\"\` to show context
- Delete old logs as needed: \`rm ./build/logs/logs-*.txt\`
For full docs: \`./\.claude/building-with-xcode.md\`"
if [ -f "${TARGET_DIR}/AGENTS.md" ]; then
if ! grep -q "xcode-cli-tools\|Xcode CLI Tools Integration" "${TARGET_DIR}/AGENTS.md"; then
echo "" >> "${TARGET_DIR}/AGENTS.md"
echo "$AGENTS_CONTENT" >> "${TARGET_DIR}/AGENTS.md"
echo " ✅ AGENTS.md updated"
else
echo " ℹ️ AGENTS.md already contains Xcode tools info"
fi
else
echo "# Agent Instructions
$AGENTS_CONTENT" > "${TARGET_DIR}/AGENTS.md"
echo " ✅ AGENTS.md created"
fi
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo -e "${GREEN}✅ Installation Complete!${NC}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo -e "${BLUE}📂 Files Created:${NC}"
echo " ./.claude/scripts/capture-logs"
echo " ./.claude/scripts/stop-logs"
echo " ./.claude/scripts/xcodebuild"
echo " ./.claude/building-with-xcode.md"
echo ""
echo -e "${BLUE}📁 Directories Created:${NC}"
echo " ./.claude/scripts/"
echo " ./build/logs/"
echo " ./build/xcodebuild/"
echo ""
echo -e "${YELLOW}🚀 Quick Start:${NC}"
echo ""
echo " 1. Build your app:"
echo " ./claude/scripts/xcodebuild -workspace MyApp.xcworkspace -scheme MyApp build"
echo ""
echo " 2. Capture logs while testing:"
echo " ./claude/scripts/capture-logs com.yourapp.vision"
echo " # Launch app in Xcode (Cmd+R)"
echo " ./claude/scripts/stop-logs"
echo ""
echo " 3. Find errors:"
echo " grep -i error ./build/xcodebuild/build-*.txt"
echo " grep -i error ./build/logs/logs-*.txt"
echo ""
echo -e "${BLUE}📖 Documentation:${NC}"
echo " Read: ./.claude/building-with-xcode.md"
echo ""
echo -e "${GREEN}Happy building! 🎉${NC}"