Compilation Flow
November 22, 2025 · View on GitHub
Overview
OpenPLC Runtime v4 uses a modern compilation flow where users upload a ZIP file generated by OpenPLC Editor v4, and the runtime automatically compiles and loads the PLC program.
Build Pipeline
1. Program Creation in OpenPLC Editor
Users create their PLC program in OpenPLC Editor v4:
- Write ladder logic, structured text, or other IEC 61131-3 languages
- Define location variables (e.g., %IX0.0, %QX0.0)
- Click "Compile" in the editor (local compilation: JSON → XML → ST → C)
- Click "Upload to Runtime" - Editor packages source files into program.zip and uploads
2. ZIP File Upload
The OpenPLC Editor uploads the ZIP file via REST API:
Endpoint: POST /api/upload-file
Authentication: JWT token (Bearer header)
File Contents:
LOCATED_VARIABLES.h- Variable location definitionsConfig0.c- PLC configurationRes0.c- Resource configurationdebug.c- Debug support codeglueVars.c- Variable binding codec_blocks_code.cpp- Custom C/C++ function blockslib/- Header files for IEC 61131-3 types
3. Security Validation
Before extraction, the ZIP file undergoes security checks:
Checks Performed:
- Valid ZIP file format
- No path traversal attempts (
.., absolute paths,:) - File size limits (10 MB per file, 50 MB total)
- Compression ratio check (ZIP bomb detection, max ratio 1000:1)
- Extension whitelist (blocks .exe, .dll, .sh, .bat, .js, .vbs, .scr)
Implementation: webserver/plcapp_management.py:analyze_zip()
4. Safe Extraction
If validation passes, files are extracted to core/generated/:
Extraction Features:
- Strips macOS metadata (
__MACOSX/,.DS_Store) - Auto-removes single root folder if present
- Validates extraction paths stay within destination
- Creates necessary subdirectories
Implementation: webserver/plcapp_management.py:safe_extract()
5. Compilation
The compilation process runs in a background thread:
Step 5a: Compile Script
Script: scripts/compile.sh
Process:
-
Validates required files exist:
Config0.cRes0.cdebug.cglueVars.clib/directory
-
Compiles each source file to object code:
gcc -w -O3 -fPIC -I core/generated/lib -c Config0.c -o build/Config0.o gcc -w -O3 -fPIC -I core/generated/lib -c Res0.c -o build/Res0.o gcc -w -O3 -fPIC -I core/generated/lib -c debug.c -o build/debug.o gcc -w -O3 -fPIC -I core/generated/lib -c glueVars.c -o build/glueVars.o g++ -w -O3 -fPIC -I core/generated/lib -c c_blocks_code.cpp -o build/c_blocks_code.o -
Links object files into shared library:
g++ -w -O3 -fPIC -shared -o build/new_libplc.so \ build/Config0.o build/Res0.o build/debug.o \ build/glueVars.o build/c_blocks_code.o
Compiler Flags:
-w- Suppress warnings-O3- Maximum optimization-fPIC- Position-independent code (required for shared libraries)
Step 5b: Cleanup Script
Script: scripts/compile-clean.sh
Process:
- Stops the PLC runtime (if running)
- Removes old library files:
build/libplc_*.so - Renames
build/new_libplc.sotobuild/libplc_<timestamp>.so - Removes object files:
build/*.o
Timestamp Format: Unix epoch seconds (e.g., libplc_1700000000.so)
Purpose: Unique library names prevent caching issues and allow rollback
6. Build Status Tracking
The build process maintains state throughout compilation:
States:
IDLE- No build in progressUNZIPPING- Extracting ZIP fileCOMPILING- Running compilation scriptsSUCCESS- Build completed successfullyFAILED- Build failed (see logs)
Status Endpoint: GET /api/compilation-status
Response:
{
"status": "SUCCESS",
"logs": ["[INFO] Starting compilation", "..."],
"exit_code": 0
}
7. Program Loading
If compilation succeeds, the runtime automatically loads the new program:
- Stop PLC: Runtime transitions to STOPPED state
- Unload Old Program: Previous library unloaded via
dlclose() - Load New Library:
dlopen()loadslibplc_<timestamp>.so - Resolve Symbols:
dlsym()resolves function pointers:ext_config_init__()- Initializationext_config_run__()- Scan cycle executionext_get_var_*()- Variable metadata functions
- Initialize: Calls
ext_config_init__() - Start PLC: Runtime transitions to RUNNING state
Implementation: core/src/plc_app/plc_state_manager.c
Compilation Logs
Build output is streamed in real-time to the client:
- stdout - Normal compilation messages
- stderr - Error messages (prefixed with
[ERROR])
Logs are buffered in build_state.logs and accessible via the compilation-status endpoint.
Error Handling
Validation Failures
If ZIP validation fails:
- Build status set to
FAILED - Descriptive error message returned
- No files extracted
Compilation Failures
If compilation fails:
- Build status set to
FAILED - Compiler error messages captured in logs
- Exit code preserved for debugging
- Old PLC program remains loaded (if any)
Recovery
After a failed build:
- Fix issues in OpenPLC Editor
- Generate new ZIP file
- Upload again (previous build state cleared automatically)
File Locations
Source Files
- Upload destination:
core/generated/ - Temporary files: Cleaned after compilation
Build Artifacts
- Object files:
build/*.o(removed after linking) - Shared library:
build/libplc_<timestamp>.so - Executable:
build/plc_main(runtime core)
Logs
- Build logs: In-memory buffer, accessible via API
- Runtime logs: Unix socket to web server
Manual Compilation (Development)
For development and testing, you can compile manually:
Prerequisites
sudo apt-get install build-essential cmake python3
Steps
-
Prepare source files in
core/generated/:- Generate from OpenPLC Editor or extract from ZIP
-
Run compilation:
bash scripts/compile.sh -
Run cleanup:
bash scripts/compile-clean.sh -
Restart runtime:
sudo bash start_openplc.sh
Advanced Topics
Custom Compiler Flags
To modify compiler flags, edit scripts/compile.sh:
FLAGS="-w -O3 -fPIC" # Current flags
Common modifications:
-g- Add debug symbols-O0- Disable optimization for debugging-Wall- Enable all warnings
Cross-Compilation
For cross-compilation to different architectures:
- Install cross-compiler toolchain
- Modify
scripts/compile.shto use cross-compiler - Set appropriate architecture flags
Example for ARM:
CC=arm-linux-gnueabihf-gcc
CXX=arm-linux-gnueabihf-g++
Library Dependencies
The compiled PLC program may depend on:
- IEC 61131-3 runtime - Provided by header files in
lib/ - Math library - Link with
-lmif using math functions - Thread library - Link with
-lpthreadif using threads
Add dependencies in scripts/compile.sh linking step.
Troubleshooting
Missing Source Files
Error: [ERROR] Missing required source files
Solution: Ensure ZIP file contains all required files from OpenPLC Editor
Compilation Errors
Error: Compiler errors in logs
Solution:
- Check PLC program syntax in OpenPLC Editor
- Verify custom C/C++ code in function blocks
- Review compilation logs for specific errors
Library Loading Failures
Error: Failed to load PLC program
Solution:
- Check library file exists:
ls -la build/libplc_*.so - Verify library is valid:
file build/libplc_*.so - Check runtime logs for dlopen errors
Permission Errors
Error: Permission denied during compilation
Solution:
- Ensure write permissions on
build/directory - Run with appropriate privileges
- Check SELinux/AppArmor policies
Related Documentation
- Editor Integration - How OpenPLC Editor uploads programs
- Architecture - System overview
- API Reference - Upload endpoint details
- Security - File validation details
- Troubleshooting - Common issues