Date: 2025-05-23
Status: Accepted
Context
The Codexis build system previously operated with isolated, per-format builds where each output format (PDF, HTML, EPUB, DOCX, etc.) created its own temporary directory and independently processed LaTeX sources. This approach led to several inefficiencies:
- Redundant LaTeX Processing: Each format builder compiled LaTeX from scratch, generating duplicate auxiliary files (.aux, .toc, .bbl, .tdo, etc.)
- Resource Waste: Multiple temporary directories consuming disk space and I/O bandwidth
- Build Time Overhead: Sequential builds taking longer due to repeated LaTeX compilation
- Inconsistent Auxiliary Files: Different formats potentially using different versions of auxiliary files
- Complex Cleanup: Each builder managing its own cleanup logic independently
Decision
We will implement a Unified Build System with Shared Temporary Directory that centralizes temporary file management and eliminates redundant processing through the following architecture:
1. BuildOrchestrator Architecture
defmodule Codexis.BuildManager.BuildOrchestrator do
@moduledoc """
Manages a shared temporary directory for all format builds in a session.
Eliminates redundant LaTeX processing by coordinating auxiliary file sharing.
"""
# Directory structure:
# temp_root/codexis_build_<timestamp>_<random>/
# ├── source/ # Source files copied here
# ├── aux/ # LaTeX auxiliary files (.aux, .toc, .bbl, etc.)
# ├── output/ # Format-specific outputs
# │ ├── pdf/
# │ ├── html/
# │ ├── epub/
# │ └── ...
# ├── assets/ # Images, fonts, etc.
# ├── images/ # Processed images
# ├── styles/ # Style files
# └── logs/ # Build logs
end
2. Build Process Flow
graph TD
A[BuildManager.build/2] --> B[BuildOrchestrator.create/2]
B --> C[Copy source files to temp/source]
C --> D[Build PDF first - generates aux files]
D --> E[Other formats read from shared aux/]
E --> F[All formats write to temp/output/format/]
F --> G[Copy outputs to final destination]
G --> H[Cleanup or preserve temp directory]
3. Key Components
a. BuildOrchestrator Module
defmodule Codexis.BuildManager.BuildOrchestrator do
@type t :: %__MODULE__{
base_dir: String.t(),
source_dir: String.t(),
aux_dir: String.t(),
output_dir: String.t(),
assets_dir: String.t(),
images_dir: String.t(),
styles_dir: String.t(),
logs_dir: String.t(),
cleanup_on_success: boolean(),
preserve_on_error: boolean()
}
def create(source_dir, opts \\ []) do
# Creates unique temp directory with secure permissions (0700)
# Sets up standardized subdirectory structure
# Copies source files to temp location
end
def cleanup(orchestrator) do
# Handles cleanup based on policy (success/error/preserve)
end
# Directory accessors
def source_dir(orchestrator), do: orchestrator.source_dir
def aux_dir(orchestrator), do: orchestrator.aux_dir
def output_dir(orchestrator), do: orchestrator.output_dir
# ... additional accessors
end
b. Refactored Build Pipeline
defmodule Codexis.BuildManager do
def build(source_dir, output_dir, options \\ %{}) do
with {:ok, orchestrator} <- BuildOrchestrator.create(source_dir, options),
{:ok, formats} <- determine_formats(options),
{:ok, _results} <- build_formats_unified(orchestrator, formats, output_dir, options) do
# Cleanup based on policy
if options[:cleanup_temp_dir] != false do
BuildOrchestrator.cleanup(orchestrator)
end
{:ok, "Build completed successfully"}
end
end
defp build_formats_unified(orchestrator, formats, output_dir, options) do
# 1. Build PDF first (generates all auxiliary files)
with {:ok, _pdf} <- build_pdf_with_orchestrator(orchestrator, output_dir, options) do
# 2. Build other formats concurrently using shared aux files
other_formats = List.delete(formats, :pdf)
Task.async_stream(other_formats, fn format ->
build_format_with_orchestrator(format, orchestrator, output_dir, options)
end, timeout: 300_000)
|> Enum.to_list()
end
end
end
c. Builder Refactoring
All format builders (PDF, HTML, EPUB, DOCX, Markdown, RTF, WikiJS) have been refactored to:
- Accept a
BuildOrchestratorparameter - Read source files from
orchestrator.source_dir - Read auxiliary files from
orchestrator.aux_dir - Write outputs to
orchestrator.output_dir/<format>/ - Eliminate their own temporary directory creation
defmodule Codexis.Builders.HTMLBuilder do
def build(orchestrator, output_path, options \\ %{}) do
source_dir = BuildOrchestrator.source_dir(orchestrator)
aux_dir = BuildOrchestrator.aux_dir(orchestrator)
# Use shared auxiliary files - no LaTeX recompilation needed
# Process using existing aux files from PDF build
end
end
4. CLI Enhancements
New command-line flags support temp directory management:
# Display temp directory path for debugging
./codexis build --show-temp-dir --source manuscript --output output
# Preserve temp directory after build
./codexis build --preserve-temp-dir --source manuscript --output output
# Specify custom temp directory root
./codexis build --temp-dir-root /custom/temp/path --source manuscript --output output
# All formats using unified system
./codexis build --all --source manuscript --output output
5. Configuration Options
The unified build system supports flexible configuration:
config :codexis, :build_orchestrator,
temp_dir_root: System.tmp_dir!(),
cleanup_on_success: true,
preserve_on_error: true,
secure_permissions: true,
concurrent_builds: true
Implementation Details
Directory Structure
The BuildOrchestrator creates a standardized directory hierarchy:
/tmp/codexis_build_1748057072_abc123de/
├── source/manuscript_source/ # Source files copied here
│ ├── manuscript.tex
│ ├── content/
│ ├── images/
│ └── meta.yaml
├── aux/ # LaTeX auxiliary files
│ ├── manuscript.aux
│ ├── manuscript.toc
│ ├── manuscript.bbl
│ ├── manuscript.tdo
│ └── ...
├── output/ # Format-specific outputs
│ ├── pdf/
│ ├── html/
│ ├── epub/
│ ├── docx/
│ └── markdown/
├── assets/ # Processed assets
├── images/ # Image processing
├── styles/ # Style files
└── logs/ # Build logs
Security Model
- Temp directories created with restrictive permissions (0700 - owner only)
- Random suffixes prevent directory name collisions
- Configurable cleanup policies for security
- Temp directory location configurable for CI/containerized environments
Error Handling
- Comprehensive error recovery mechanisms
- Preservation of temp directory on build failures for debugging
- Graceful cleanup of partially created directory structures
- Detailed logging of all file operations
Performance Optimizations
- Eliminated Redundant Processing: LaTeX compiled once, auxiliary files shared
- Concurrent Format Building: Non-PDF formats built in parallel after PDF completion
- Reduced I/O: Single source file copy, shared auxiliary file access
- Memory Efficiency: Controlled resource allocation per build session
Testing Strategy
Comprehensive test coverage ensures system reliability:
Test Suites Implemented
-
Unit Tests (
test/codexis/build_manager/build_orchestrator_test.exs)- BuildOrchestrator creation and configuration
- Directory structure and permissions
- Source file copying and management
- Cleanup and preservation policies
- Error handling scenarios
-
Integration Tests (
test/codexis/build_manager_test.exs)- BuildManager with orchestrator integration
- Multi-format builds through unified system
- Error propagation and recovery
-
End-to-End Tests (
test/integration/unified_build_system_test.exs)- Complete build pipeline testing
- Multi-format output validation
- Auxiliary file reuse verification
-
Performance Tests (
test/performance/build_performance_test.exs)- Build time benchmarking
- Memory usage monitoring
- Unified vs sequential build comparison
-
Validation Tests (
test/validation/cross_format_consistency_test.exs)- Content consistency across formats
- Mathematical content preservation
- Cross-reference integrity
Test Results
- Unit Tests: 95%+ pass rate (20/21 tests passing)
- Integration Tests: ✅ All BuildManager integration verified
- Performance Tests: ✅ Measurable efficiency gains confirmed
- Validation Tests: ✅ Content consistency maintained across formats
Consequences
Positive
- Performance: Significant build time reduction through eliminated redundant LaTeX processing
- Efficiency: Reduced disk I/O and memory usage
- Consistency: All formats use identical auxiliary files ensuring consistency
- Maintainability: Centralized temp directory management simplifies cleanup and debugging
- Scalability: Better resource utilization for large documents and multiple format builds
- Reliability: Comprehensive error handling and recovery mechanisms
Negative
- Complexity: Increased coordination complexity between builders
- Dependencies: PDF build must complete before other formats can proceed
- Debugging: More complex debugging when issues span multiple formats
Migration Path
- Backward Compatibility: Existing CLI commands continue to work unchanged
- Gradual Adoption: New unified system used automatically for multi-format builds
- Fallback: Individual format builds still supported for specific use cases
Compliance
The unified build system maintains full compliance with:
- Output Quality: All formats produce identical quality to previous system
- Feature Parity: No reduction in functionality or supported features
- Configuration: All existing configuration options preserved
- CLI Compatibility: Existing command-line interfaces unchanged
- Performance: Measurable improvement in build times and resource usage
References
- ADR-0024: Build Pipeline Architecture
- Task #17: Design and Implement a Unified Build System
- "Elixir OTP Supervision Strategies" - Elixir Documentation
- "GenServer and Process Management" - Elixir in Action, Saša Jurić
- "File System Operations and Security" - Erlang/OTP Documentation