-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuild.sh
More file actions
executable file
·411 lines (336 loc) · 11.7 KB
/
build.sh
File metadata and controls
executable file
·411 lines (336 loc) · 11.7 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
#!/bin/bash
# build.sh - robust build script for typg on macOS
# made by FontLab https://www.fontlab.com/
set -euo pipefail
# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$SCRIPT_DIR"
TARGET_DIR="$PROJECT_ROOT/target"
BUILD_TYPE="${1:-release}" # Default to release build
PYTHON_VERSION="${2:-3.13}" # Default Python version for bindings
SKIP_PYTHON=false # Set to true if maturin is missing
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Logging functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Usage information
usage() {
cat << EOF
Usage: $0 [BUILD_TYPE] [PYTHON_VERSION]
Build script for typg - ultra-fast font search/discovery toolkit
BUILD_TYPE:
debug Build with debugging information (faster compile)
release Optimized release build (default)
check Run checks without building
clean Clean build artifacts
PYTHON_VERSION:
3.10, 3.11, 3.12, or 3.13 (default: 3.13)
Used only for Python bindings compilation
Examples:
$0 # Release build with Python 3.13
$0 debug # Debug build with Python 3.13
$0 release 3.12 # Release build with Python 3.12
$0 clean # Clean build artifacts
EOF
}
# Check dependencies
check_dependencies() {
log_info "Checking dependencies..."
local missing_deps=()
# Check for Rust toolchain
if ! command -v cargo >/dev/null 2>&1; then
missing_deps+=("cargo")
fi
if ! command -v rustc >/dev/null 2>&1; then
missing_deps+=("rustc")
fi
# Check for Python
if ! command -v "python${PYTHON_VERSION}" >/dev/null 2>&1; then
missing_deps+=("python${PYTHON_VERSION}")
fi
# Check for maturin (needed for Python bindings, warn only)
if ! command -v maturin >/dev/null 2>&1; then
log_warning "maturin not found - Python bindings will be skipped"
log_info " Install with: pip${PYTHON_VERSION} install maturin"
SKIP_PYTHON=true
fi
if [[ ${#missing_deps[@]} -gt 0 ]]; then
log_error "Missing dependencies: ${missing_deps[*]}"
log_info "Install missing dependencies:"
if [[ " ${missing_deps[*]} " =~ " cargo " ]] || [[ " ${missing_deps[*]} " =~ " rustc " ]]; then
log_info " Rust: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
fi
if [[ " ${missing_deps[*]} " =~ " python${PYTHON_VERSION} " ]]; then
log_info " Python: Install via Homebrew or python.org"
fi
return 1
fi
log_success "All dependencies found"
return 0
}
# Sync Cargo.toml versions to the latest vN.N.N git tag.
# Runs on every build so versions stay current automatically.
sync_cargo_versions() {
local tag
tag=$(git -C "$PROJECT_ROOT" describe --tags --abbrev=0 --match 'v[0-9]*' 2>/dev/null) || {
log_warning "No git tag found — skipping version sync"
return 0
}
local version="${tag#v}"
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
log_warning "Tag '$tag' is not semver — skipping version sync"
return 0
fi
local current
current=$(grep '^version = ' "$PROJECT_ROOT/core/typg-core/Cargo.toml" | sed 's/version = "\(.*\)"/\1/' | head -n1)
if [[ "$current" == "$version" ]]; then
return 0
fi
log_info "Syncing Cargo.toml versions to $version (from tag $tag)"
perl -0pi -e "s/^version = \"[^\"]*\"/version = \"${version}\"/m" "$PROJECT_ROOT/core/typg-core/Cargo.toml"
perl -0pi -e "s/^version = \"[^\"]*\"/version = \"${version}\"/m" "$PROJECT_ROOT/cli/Cargo.toml"
perl -0pi -e "s/^version = \"[^\"]*\"/version = \"${version}\"/m" "$PROJECT_ROOT/py/typg-python/Cargo.toml"
perl -0pi -e "s/typg-core = \{[^}]*path = \"\.\.\/core\/typg-core\"[^}]*\}/typg-core = { version = \"=${version}\", path = \"..\/core\/typg-core\" }/g" "$PROJECT_ROOT/cli/Cargo.toml"
perl -0pi -e "s/typg-core = \{[^}]*path = \"\.\.\/\.\.\/core\/typg-core\"[^}]*\}/typg-core = { version = \"=${version}\", path = \"..\/..\/core\/typg-core\" }/g" "$PROJECT_ROOT/py/typg-python/Cargo.toml"
}
# Detect macOS architecture
detect_arch() {
local arch=$(uname -m)
case "$arch" in
arm64)
echo "aarch64-apple-darwin"
;;
x86_64)
echo "x86_64-apple-darwin"
;;
*)
log_error "Unsupported architecture: $arch"
return 1
;;
esac
}
# Clean build artifacts
clean_build() {
log_info "Cleaning build artifacts..."
# Clean Rust workspace
cargo clean
# Clean Python build artifacts if they exist
if [[ -f "$PROJECT_ROOT/py/typg-python/pyproject.toml" ]]; then
(cd "$PROJECT_ROOT/py/typg-python" && rm -rf build/ dist/ *.egg-info/)
fi
log_success "Build artifacts cleaned"
}
# Run checks without building
run_checks() {
log_info "Running code quality checks..."
local failed_checks=0
# Rust formatting check (only check workspace members, exclude linked)
log_info "Checking Rust formatting..."
if cargo fmt -p typg-core -p typg-cli -p typg-python -- --check; then
log_success "Formatting check passed"
else
log_error "Formatting check failed"
((failed_checks++))
fi
# Rust linting (only workspace members, exclude benchmarks and linked)
log_info "Running Rust linter..."
if cargo clippy -p typg-core -p typg-cli -p typg-python --all-targets --all-features -- -W warnings; then
log_success "Linting check passed"
else
log_error "Linting check failed (warnings found)"
((failed_checks++))
fi
if [[ $failed_checks -eq 0 ]]; then
log_success "All checks passed"
return 0
else
log_warning "$failed_checks check(s) failed. Fix issues before proceeding with release build."
return 1
fi
}
# Build Rust components
build_rust() {
local build_flags=""
if [[ "$BUILD_TYPE" == "release" ]]; then
build_flags="--release"
log_info "Building Rust workspace in release mode..."
else
log_info "Building Rust workspace in debug mode..."
fi
# Build workspace (excluding Python bindings which need maturin)
cargo build $build_flags --workspace --exclude typg-python
log_success "Rust components built successfully"
}
# Build Python bindings
build_python() {
log_info "Building Python bindings for Python $PYTHON_VERSION..."
cd "$PROJECT_ROOT/py/typg-python"
# Verify we're targeting the right Python version
local python_exe="python${PYTHON_VERSION}"
local python_lib_dir=$("$python_exe" -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))")
if [[ -z "$python_lib_dir" ]]; then
log_error "Could not determine Python library directory for Python $PYTHON_VERSION"
return 1
fi
log_info "Using Python library directory: $python_lib_dir"
# Build Python extension
local target=$(detect_arch)
log_info "Targeting architecture: $target"
# Determine if we're building release or debug
local maturin_release_flag=""
if [[ "$BUILD_TYPE" == "release" ]]; then
maturin_release_flag="--release"
fi
# Build using maturin
maturin build $maturin_release_flag \
--target $target \
--interpreter "python${PYTHON_VERSION}" \
--features extension-module
cd "$PROJECT_ROOT"
log_success "Python bindings built successfully"
}
# Install Python bindings locally (development)
install_python_dev() {
log_info "Installing Python bindings in development mode..."
cd "$PROJECT_ROOT/py/typg-python"
# Try to install in development mode using maturin develop
if maturin develop --features extension-module 2>/dev/null; then
log_success "Python bindings installed in development mode via maturin develop"
else
# If maturin develop fails, build wheel and install with pip
log_warning "No virtualenv detected, building wheel and installing with pip..."
# Build wheel
local target=$(detect_arch)
local maturin_release_flag=""
if [[ "$BUILD_TYPE" == "release" ]]; then
maturin_release_flag="--release"
fi
maturin build $maturin_release_flag \
--target $target \
--interpreter "python${PYTHON_VERSION}" \
--features extension-module
# Find the built wheel and install it
local wheel_file=$(find "$PROJECT_ROOT/target/wheels" -name "typg-*.whl" -type f | head -n1)
if [[ -n "$wheel_file" ]]; then
"pip${PYTHON_VERSION}" install "$wheel_file" --user
log_success "Python bindings installed via pip install"
else
log_error "Could not find built wheel to install"
cd "$PROJECT_ROOT"
return 1
fi
fi
cd "$PROJECT_ROOT"
}
# Verify build
verify_build() {
log_info "Verifying build..."
# Check if core library was built
local target_path="$TARGET_DIR"
if [[ "$BUILD_TYPE" == "release" ]]; then
target_path="$target_path/release"
else
target_path="$target_path/debug"
fi
if [[ ! -f "$target_path/libtypg_core.a" ]] && [[ ! -f "$target_path/libtypg_core.rlib" ]]; then
log_error "Core library not found at $target_path"
return 1
fi
# Check CLI binary
if [[ ! -f "$target_path/typg" ]]; then
log_error "CLI binary not found at $target_path/typg"
return 1
fi
log_success "Build verification passed"
}
# Main build function
main() {
log_info "Starting typg build process..."
log_info "Project root: $PROJECT_ROOT"
log_info "Build type: $BUILD_TYPE"
# Handle special commands
case "$BUILD_TYPE" in
help|--help|-h)
usage
exit 0
;;
check|--check )
check_dependencies
run_checks
exit 0
;;
clean|--clean)
check_dependencies
clean_build
exit 0
;;
debug|--debug)
BUILD_TYPE="debug"
# Valid build types, continue
;;
release|--release)
BUILD_TYPE="release"
# Valid build types, continue
;;
*)
log_error "Unknown build type: $BUILD_TYPE"
usage
exit 1
;;
esac
# Dependencies check
check_dependencies
# Sync Cargo.toml versions from git tag
sync_cargo_versions
# Build components
build_rust
if [[ "$SKIP_PYTHON" != "true" ]]; then
build_python
fi
# Verify
verify_build
log_success "Build completed successfully!"
# Show artifact locations
local target_path="$TARGET_DIR"
if [[ "$BUILD_TYPE" == "release" ]]; then
target_path="$target_path/release"
else
target_path="$target_path/debug"
fi
echo
log_info "Build artifacts:"
echo " Core library: $target_path/libtypg_core.rlib"
echo " CLI binary: $target_path/typg"
echo " Python wheel: $TARGET_DIR/wheels/"
echo
log_info "To install Python bindings in development mode, run:"
echo " $0 dev-install $BUILD_TYPE $PYTHON_VERSION"
}
# Additional commands
case "${1:-}" in
dev-install)
BUILD_TYPE="${2:-release}"
PYTHON_VERSION="${3:-3.13}"
check_dependencies
install_python_dev
exit 0
;;
*)
main
;;
esac