Skip to content

WSL Build Guide for nativify

This guide provides step-by-step instructions for building and testing nativify in WSL 2 Ubuntu, bypassing Windows LLVM issues.

System Configuration: - WSL Version: WSL 2 - Distribution: Ubuntu 20.04 or later - Project Location: /mnt/d/projects/masxinling-plus (Windows D:\ drive) - Target Platform: linux-x86_64

Estimated Time: 15-30 minutes (depending on download speeds)


Table of Contents

  1. Prerequisites Check
  2. Phase 1: WSL Environment Setup
  3. Phase 2: Install Java 21
  4. Phase 3: Install Maven
  5. Phase 4: Install LLVM 20 and Clang
  6. Phase 5: Configure Environment Variables
  7. Phase 6: Build the Project
  8. Phase 7: Run Tests
  9. Phase 8: Verify Output
  10. Troubleshooting
  11. Performance Optimization (Optional)

Prerequisites Check

Before starting, ensure you have: - Complete WSL 2 installed on Windows - Complete Ubuntu distribution installed in WSL - Complete Windows project files accessible at D:\projects\masxinling-plus

To verify WSL version:

# Run in Windows PowerShell
wsl --list --verbose

Should show WSL version 2.


Phase 1: WSL Environment Setup

Step 1.1: Open WSL Terminal

Open Ubuntu terminal (you can use Windows Terminal or search for "Ubuntu" in Start menu).

Step 1.2: Navigate to Project Directory

cd /mnt/d/projects/masxinling-plus

Step 1.3: Verify Access to Project Files

ls -la

Expected output: You should see files like pom.xml, README.md, CLAUDE.md, etc.


Phase 2: Install Java 21

Step 2.1: Update Package Lists

sudo apt update

What this does: Updates the list of available packages and their versions.

Step 2.2: Install OpenJDK 21

sudo apt install -y openjdk-21-jdk

What this does: Installs Java Development Kit 21, required for building the project.

Note: This may take 2-5 minutes depending on your connection.

Step 2.3: Verify Java Installation

java -version

Expected output:

openjdk version "21.x.x" ...
OpenJDK Runtime Environment ...
OpenJDK 64-Bit Server VM ...

Step 2.4: Verify Java Compiler

javac -version

Expected output:

javac 21.x.x

echo $JAVA_HOME

If empty, set it:

export JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java))))
echo $JAVA_HOME

Expected output: /usr/lib/jvm/java-21-openjdk-amd64 (or similar)


Phase 3: Install Maven

Step 3.1: Install Maven

sudo apt install -y maven

What this does: Installs Apache Maven build tool.

Note: This may take 1-3 minutes.

Step 3.2: Verify Maven Installation

mvn -version

Expected output:

Apache Maven 3.x.x
Maven home: /usr/share/maven
Java version: 21.x.x, vendor: ...

Important: Verify that Maven is using Java 21 (shown in output).


Phase 4: Install LLVM 20 and Clang

Step 4.1: Install LLVM 20

sudo apt install -y llvm-20

What this does: Installs LLVM 20 compiler infrastructure.

Note: This may take 3-7 minutes depending on connection.

Step 4.2: Install Clang 20

sudo apt install -y clang-20

What this does: Installs Clang C/C++ compiler (required for native code generation).

Step 4.3: Verify LLVM Installation

llc-20 --version

Expected output:

LLVM (http://llvm.org/):
 LLVM version 20.x.x
 ...

Step 4.4: Verify Clang Installation

clang-20 --version

Expected output:

Ubuntu clang version 20.x.x
Target: x86_64-pc-linux-gnu

Step 4.5: Verify Other LLVM Tools

opt-20 --version

Expected output: Should show LLVM version 20.x.x


Phase 5: Configure Environment Variables

Step 5.1: Add LLVM to PATH (Temporary)

export PATH=/usr/lib/llvm-20/bin:$PATH

What this does: Makes LLVM tools available without version suffix (e.g., llc instead of llc-20).

Step 5.2: Verify PATH Configuration

which llc

Expected output: /usr/lib/llvm-20/bin/llc

Test without version suffix:

llc --version

Should show LLVM 20.x.x

Step 5.3: Make PATH Permanent

Add LLVM to your shell configuration:

echo 'export PATH=/usr/lib/llvm-20/bin:$PATH' >> ~/.bashrc

What this does: Ensures LLVM is in PATH every time you open a new WSL terminal.

Step 5.4: Reload Shell Configuration (Optional)

source ~/.bashrc

Or simply close and reopen your WSL terminal.

Step 5.5: Set JAVA_HOME Permanently (If Not Already Set)

echo 'export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64' >> ~/.bashrc

Adjust path if your Java installation is different (check with which java).


Phase 6: Build the Project

Step 6.1: Navigate to Project Directory

cd /mnt/d/projects/masxinling-plus

Step 6.2: Clean Any Previous Windows Builds

mvn clean

What this does: Removes any artifacts from previous Windows builds that might cause conflicts.

Step 6.3: Build with Linux Platform Flag

mvn clean install -Djavacpp.platform=linux-x86_64

What this does: - clean: Removes previous build artifacts - install: Builds all modules and installs to local Maven repository - -Djavacpp.platform=linux-x86_64: Tells ByteDeco to use Linux native libraries

Expected Duration: 3-10 minutes on first build

Expected Output (at the end):

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: X min XX s

Step 6.4: Verify Build Artifacts

ls -lh nativify-cli/target/nativify-cli-*.jar

Expected output: Should show the CLI JAR file (approximately 50-100 MB).


Phase 7: Run Tests

Step 7.1: Verify Test JAR Exists

ls -lh obf-test.jar

Expected output: obf-test.jar should exist in the project root.

Step 7.2: Run Obfuscation on Test JAR

mvn exec:java -pl nativify-cli \
 -Dexec.mainClass="dev.haedus.nativify.CLIMain" \
 -Dexec.args="-i obf-test.jar -o obfuscated.jar -compileFor linux"

What this does: - Takes obf-test.jar as input - Compiles marked methods to native Linux code - Generates obfuscated.jar with native libraries embedded

Expected Duration: 1-5 minutes

Expected Output:

[INFO] Processing input JAR: obf-test.jar
[INFO] Compiling methods to LLVM IR...
[INFO] Running LLVM optimizations...
[INFO] Generating native library for linux...
[INFO] Writing output JAR: obfuscated.jar
[INFO] BUILD SUCCESS

Step 7.3: Verify Obfuscated JAR Created

ls -lh obfuscated.jar

Expected output: obfuscated.jar should exist and be similar size to obf-test.jar.

Step 7.4: Check Native Libraries in JAR

jar tf obfuscated.jar | grep "META-INF/natives"

Expected output: Should show .so files (Linux shared libraries):

META-INF/natives/linux-x86_64/
META-INF/natives/linux-x86_64/libmlv_native_xxx.so


Phase 8: Verify Output

Step 8.1: Run Obfuscated JAR

java -jar obfuscated.jar

Expected Output (SUCCESS):

Successfully passed every test!

If you see this message: Complete Build and obfuscation successful!

Step 8.2: Alternative - Run with Verbose Output

For debugging, you can run with Java logging:

java -jar obfuscated.jar -verbose:jni

This shows JNI calls and native library loading.


Troubleshooting

Issue 1: "LLVM not found" During Build

Symptom: Error about missing llc, opt, or clang

Solution:

# Check if LLVM is in PATH
which llc

# If not found, add to PATH
export PATH=/usr/lib/llvm-20/bin:$PATH

# Verify
llc --version

Alternative: Specify LLVM directory explicitly:

mvn exec:java -pl nativify-cli \
 -Dexec.mainClass="dev.haedus.nativify.CLIMain" \
 -Dexec.args="-i obf-test.jar -o obfuscated.jar -compileFor linux -llvmDir /usr/lib/llvm-20/bin"

Issue 2: "Permission Denied" Errors

Symptom: Cannot write to directories or execute commands

Solution:

# Make sure you're not trying to write to protected Windows directories
# Work within /mnt/d/projects/... (should have write access)

# If needed, check permissions
ls -la obf-test.jar

Issue 3: Maven Build Fails with "Cannot Find Symbol"

Symptom: Compilation errors about missing classes

Solution:

# Ensure you're using Java 21
java -version

# Clean and rebuild
mvn clean install -Djavacpp.platform=linux-x86_64

Issue 4: Tests Fail - "Successfully passed every test!" Not Shown

Symptom: Obfuscated JAR runs but shows errors or different output

Possible Causes: 1. Sign extension issues (should be fixed in latest code) 2. Missing native libraries 3. Platform mismatch

Debug Steps:

# Generate LLVM IR for inspection
mvn exec:java -pl nativify-cli \
 -Dexec.mainClass="dev.haedus.nativify.CLIMain" \
 -Dexec.args="-i obf-test.jar -o obfuscated.jar -ll output.ll"

# Check IR file
less output.ll

Issue 5: Slow Build Performance

Symptom: Build takes much longer than expected

Cause: Cross-filesystem access between Windows and WSL can be slow.

Solutions: 1. Temporary: Use WSL 2's improved I/O (should be fast enough) 2. Long-term: Copy project to WSL filesystem (see Performance Optimization below)

Issue 6: UnsatisfiedLinkError When Running JAR

Symptom:

java.lang.UnsatisfiedLinkError: no mlv_native_xxx in java.library.path

Solution:

# Verify native libraries are in JAR
jar tf obfuscated.jar | grep ".so"

# If missing, rebuild with correct platform flag
mvn clean install -Djavacpp.platform=linux-x86_64


Performance Optimization (Optional)

If builds are too slow accessing files from /mnt/d/..., consider copying the project to WSL's native filesystem:

Option A: Copy to WSL Filesystem

# Create projects directory in WSL home
mkdir -p ~/projects

# Copy project (preserves permissions and attributes)
cp -r /mnt/d/projects/masxinling-plus ~/projects/

# Navigate to new location
cd ~/projects/masxinling-plus

# Build (will be faster)
mvn clean install -Djavacpp.platform=linux-x86_64

Pros: - Complete Much faster builds (native filesystem I/O) - Complete Better performance for Git operations - Complete No cross-filesystem overhead

Cons: - Incomplete Need to keep Windows and WSL copies in sync - Incomplete Uses WSL disk space (ext4 virtual disk)

Option B: Enable WSL 2 Advanced Settings

Create or edit C:\Users\<YourUsername>\.wslconfig on Windows:

[wsl2]
memory=8GB
processors=4
localhostForwarding=true

Then restart WSL:

# In PowerShell
wsl --shutdown

Reopen WSL terminal - performance should improve.


Quick Reference Commands

Full Build and Test Workflow

# 1. Navigate to project
cd /mnt/d/projects/masxinling-plus

# 2. Build
mvn clean install -Djavacpp.platform=linux-x86_64

# 3. Test
mvn exec:java -pl nativify-cli \
 -Dexec.mainClass="dev.haedus.nativify.CLIMain" \
 -Dexec.args="-i obf-test.jar -o obfuscated.jar -compileFor linux"

# 4. Verify
java -jar obfuscated.jar
# Expected: "Successfully passed every test!"

Environment Variables Checklist

# Check Java
echo $JAVA_HOME
java -version

# Check Maven
mvn -version

# Check LLVM
echo $PATH | grep llvm
llc --version
opt --version
clang --version

Clean Rebuild (When Things Go Wrong)

# Clean everything
mvn clean

# Clear Maven cache (if really stuck)
rm -rf ~/.m2/repository/dev/haedus/nativify

# Rebuild from scratch
mvn clean install -Djavacpp.platform=linux-x86_64

Multi-Platform Compilation

Once WSL setup is complete, you can compile for multiple platforms:

mvn exec:java -pl nativify-cli \
 -Dexec.mainClass="dev.haedus.nativify.CLIMain" \
 -Dexec.args="-i obf-test.jar -o obfuscated.jar -compileFor windows,linux"

This generates native libraries for both Windows and Linux in the same JAR.


Success Criteria

You've successfully completed the WSL setup if:

  • Complete mvn clean install -Djavacpp.platform=linux-x86_64 completes without errors
  • Complete Obfuscation command generates obfuscated.jar with Linux .so files
  • Complete Running java -jar obfuscated.jar shows: "Successfully passed every test!"
  • Complete Future builds can be done entirely in WSL, bypassing Windows LLVM issues

Additional Notes

Why WSL Over Windows for This Project?

  1. LLVM Integration: Linux LLVM tools are more stable and easier to configure
  2. Native Library Generation: Clang on Linux handles JNI better
  3. Build Performance: Native filesystem I/O is faster
  4. Debugging: Better tooling for inspecting LLVM IR and native libraries

Keeping Windows and WSL Builds Separate

  • Windows builds: Use windows-x86_64 platform flag
  • WSL builds: Use linux-x86_64 platform flag
  • Always run mvn clean when switching between platforms

Next Steps After Setup

  1. Test with your own JAR files
  2. Experiment with optimization levels (-O0, -O1, -O2, -O3)
  3. Profile performance differences between bytecode and native code
  4. Explore LLVM IR output with -ll output.ll flag

Support and Resources

  • Project Documentation: See the CLI Reference, Usage Guide, and Benchmarks
  • LLVM Documentation: https://llvm.org/docs/
  • WSL Documentation: https://learn.microsoft.com/en-us/windows/wsl/

Last Updated: 2025-11-22 Tested On: WSL 2 Ubuntu 22.04, Java 21, Maven 3.9.x, LLVM 20.1.7