build(binding): add universal macOS ARM64 dylib and static FreeType compilation

- Static FreeType linking flow for all OS
- Flow to build universal binaries for macOS (x86_64, aarch64)
- Simplified CI workflow by removing FreeType branches from the build matrix

Added 2 new scripts:
1. `buildSrc/scripts/vendor_freetype.sh`
2. `buildSrc/scripts/build.sh`

The first script prepares FreeType for usage in the build process.
The result of this script is used in the `GenerateLibs` task.
Other ways of building with FreeType are removed.
If it's impossible to build with FreeType, it is recommended to set `freetype=false`.

The second script aggregates commands to build native binaries.
It includes specific steps, such as creating a universal library for macOS and copying the result to the specific folder `/tmp/imgui/dst`.

Script 1 can be used for local builds. Script 2 is primarily intended for use in CI.
This commit is contained in:
SpaiR committed 2024-08-07 09:41:09 +03:00
1 parent 66522fa265
commit 46c008eb02
16 files changed
+423 -220

No files matched your search

+106
View File
@@ -0,0 +1,106 @@
#!/bin/bash
echo "Setting base directory and navigating to project root..."
BASEDIR=$(dirname "$0")
cd "$BASEDIR"/../.. || exit 1
echo "Navigated to $(pwd)"
# Check if vendor type argument is provided
if [ -z "$1" ]; then
echo "Vendor type is required"
exit 1
fi
VTYPE=$1
echo "Vendor type set to '$VTYPE'"
# Make the vendor FreeType script executable and run it
echo "Making vendor FreeType script executable and running it..."
chmod +x buildSrc/scripts/vendor_freetype.sh
buildSrc/scripts/vendor_freetype.sh "$VTYPE"
if [ $? -ne 0 ]; then
echo "Vendor FreeType script failed"
exit 1
fi
echo "Vendor FreeType script completed successfully"
# Create the destination directory for imgui libraries
echo "Creating destination directory for imgui libraries..."
mkdir -p /tmp/imgui/dst
echo "Directory /tmp/imgui/dst created successfully"
# Function to check if a file exists
check_file_exists() {
if [ ! -f "$1" ]; then
echo "File $1 not found!"
exit 1
fi
}
# Determine build process based on vendor type
case "$VTYPE" in
windows)
echo "Running Gradle task for Windows..."
./gradlew imgui-binding:generateLibs -Denvs=windows -Dfreetype=true
if [ $? -ne 0 ]; then
echo "Gradle task for Windows failed"
exit 1
fi
echo "Checking if the generated DLL exists..."
check_file_exists /tmp/imgui/libsNative/windows64/imgui-java64.dll
echo "Copying the generated DLL to the destination directory..."
cp /tmp/imgui/libsNative/windows64/imgui-java64.dll /tmp/imgui/dst/imgui-java64.dll
if [ $? -ne 0 ]; then
echo "Failed to copy DLL to /tmp/imgui/dst/imgui-java64.dll"
exit 1
fi
echo "DLL copied to /tmp/imgui/dst/imgui-java64.dll successfully"
;;
linux)
echo "Running Gradle task for Linux..."
./gradlew imgui-binding:generateLibs -Denvs=linux -Dfreetype=true
if [ $? -ne 0 ]; then
echo "Gradle task for Linux failed"
exit 1
fi
echo "Checking if the generated SO file exists..."
check_file_exists /tmp/imgui/libsNative/linux64/libimgui-java64.so
echo "Copying the generated SO file to the destination directory..."
cp /tmp/imgui/libsNative/linux64/libimgui-java64.so /tmp/imgui/dst/libimgui-java64.so
if [ $? -ne 0 ]; then
echo "Failed to copy SO file to /tmp/imgui/dst/libimgui-java64.so"
exit 1
fi
echo "SO file copied to /tmp/imgui/dst/libimgui-java64.so successfully"
;;
macos)
echo "Running Gradle task for macOS and macOS ARM..."
./gradlew imgui-binding:generateLibs -Denvs=macos,macosarm64 -Dfreetype=true
if [ $? -ne 0 ]; then
echo "Gradle task for macOS failed"
exit 1
fi
echo "Checking if the generated DYLIB files exist..."
check_file_exists /tmp/imgui/libsNative/macosx64/libimgui-java64.dylib
check_file_exists /tmp/imgui/libsNative/macosxarm64/libimgui-java64.dylib
echo "Creating a universal library using lipo..."
lipo -create -output /tmp/imgui/dst/libimgui-java64.dylib /tmp/imgui/libsNative/macosx64/libimgui-java64.dylib /tmp/imgui/libsNative/macosxarm64/libimgui-java64.dylib
if [ $? -ne 0 ]; then
echo "Failed to create universal library with lipo"
exit 1
fi
echo "Universal library created at /tmp/imgui/dst/libimgui-java64.dylib successfully"
;;
*)
echo "Unknown vendor type: $VTYPE"
exit 1
;;
esac
echo "Script completed successfully."
+38 -23
View File
@@ -1,30 +1,45 @@
#!/bin/bash
# Set base directory and navigate to project root
echo "Setting base directory and navigating to project root..."
BASEDIR=$(dirname "$0")
cd $BASEDIR/../.. || exit
cd "$BASEDIR"/../.. || exit 1
echo "Navigated to $(pwd)"
echo '> Publishing Modules...'
echo '>> Module [imgui-app]'
./gradlew imgui-app:publishImguiPublicationToMavenCentralRepository
echo '>> Module [imgui-lwjgl3]'
./gradlew imgui-lwjgl3:publishImguiPublicationToMavenCentralRepository
echo '>> Module [imgui-binding]'
./gradlew imgui-binding:publishImguiPublicationToMavenCentralRepository
publish_module() {
local module=$1
echo ">> Publishing Module [$module]"
./gradlew $module:publishImguiPublicationToMavenCentralRepository
if [ $? -ne 0 ]; then
echo "Failed to publish $module module"
exit 1
fi
echo ">> Module [$module] published successfully"
}
# Publish each module
publish_module "imgui-app"
publish_module "imgui-lwjgl3"
publish_module "imgui-binding"
echo '> Publishing Natives...'
echo '>> Natives: [windows, freetype]'
./gradlew imgui-binding-natives:publishImguiPublicationToMavenCentralRepository -PdeployType=windows -Pfreetype=true
echo '>> Natives: [windows]'
./gradlew imgui-binding-natives:publishImguiPublicationToMavenCentralRepository -PdeployType=windows -Pfreetype=false
echo '>> Natives: [linux, freetype]'
./gradlew imgui-binding-natives:publishImguiPublicationToMavenCentralRepository -PdeployType=linux -Pfreetype=true
echo '>> Natives: [linux]'
./gradlew imgui-binding-natives:publishImguiPublicationToMavenCentralRepository -PdeployType=linux -Pfreetype=false
echo '>> Natives: [macOS, freetype]'
./gradlew imgui-binding-natives:publishImguiPublicationToMavenCentralRepository -PdeployType=macos -Pfreetype=true
echo '>> Natives: [macOS]'
./gradlew imgui-binding-natives:publishImguiPublicationToMavenCentralRepository -PdeployType=macos -Pfreetype=false
echo '>> Natives: [macOS-arm64, freetype]'
./gradlew imgui-binding-natives:publishImguiPublicationToMavenCentralRepository -PdeployType=macosarm64 -Pfreetype=true
echo '>> Natives: [macOS-arm64]'
./gradlew imgui-binding-natives:publishImguiPublicationToMavenCentralRepository -PdeployType=macosarm64 -Pfreetype=false
publish_natives() {
local platform=$1
echo ">> Publishing Natives: [$platform]"
./gradlew imgui-binding-natives:publishImguiPublicationToMavenCentralRepository -PdeployType=$platform
if [ $? -ne 0 ]; then
echo "Failed to publish natives for $platform"
exit 1
fi
echo ">> Natives for $platform published successfully"
}
# Publish natives for each platform
publish_natives "windows"
publish_natives "linux"
publish_natives "macos"
echo "All modules and natives published successfully."
+107
View File
@@ -0,0 +1,107 @@
#!/bin/bash
# Set base directory and navigate to project root
echo "Setting base directory and navigating to project root..."
BASEDIR=$(dirname "$0")
cd "$BASEDIR"/../.. || exit 1
echo "Navigated to $(pwd)"
# Check if vendor type argument is provided
if [ -z "$1" ]; then
echo "Vendor type is required"
exit 1
fi
VTYPE=$1
echo "Vendor type set to '$VTYPE'"
# Define library directory and version
LIBDIR=build/vendor/freetype
VERSION=2.12.1
# Clean and create library directory, then extract FreeType source
echo "Cleaning and creating library directory, then extracting FreeType source..."
rm -rf $LIBDIR
mkdir -p $LIBDIR
tar -xzf ./vendor/freetype-$VERSION.tar.gz -C $LIBDIR --strip-components=1
if [ $? -ne 0 ]; then
echo "Failed to extract FreeType source"
exit 1
fi
cd $LIBDIR || exit 1
echo "FreeType unzipped to $LIBDIR"
# Common configuration flags
COMMON_FLAGS="--enable-static --disable-shared --without-zlib --without-bzip2 --without-png --without-harfbuzz --without-brotli"
# Function to configure and build FreeType
build_freetype() {
cflags=$1
prefix=$2
output_dir=$3
echo "Cleaning previous builds..."
make clean
echo "Configuring FreeType with CFLAGS='$cflags' and PREFIX='$prefix'..."
./configure CFLAGS="$cflags" $COMMON_FLAGS $prefix
if [ $? -ne 0 ]; then
echo "Failed to configure FreeType"
exit 1
fi
echo "Building FreeType..."
make
if [ $? -ne 0 ]; then
echo "Failed to build FreeType"
exit 1
fi
echo "Checking if the generated library exists..."
if [ ! -f objs/.libs/libfreetype.a ]; then
echo "File objs/.libs/libfreetype.a not found!"
exit 1
fi
echo "Copying the generated library to $output_dir..."
cp objs/.libs/libfreetype.a "$output_dir"
if [ $? -ne 0 ]; then
echo "Failed to copy library to $output_dir"
exit 1
fi
echo "Library copied to $output_dir"
}
# Ensure necessary directories exist
echo "Ensuring necessary directories exist..."
mkdir -p lib tmp
# Determine build process based on vendor type
case "$VTYPE" in
windows)
build_freetype "" "--host=x86_64-w64-mingw32 --prefix=/usr/x86_64-w64-mingw32" "lib/libfreetype.a"
;;
linux)
build_freetype "-fPIC" "" "lib/libfreetype.a"
;;
macos)
MACOS_VERSION=10.15
build_freetype "-arch x86_64 -mmacosx-version-min=$MACOS_VERSION" "" "tmp/libfreetype-x86_64.a"
build_freetype "-arch arm64 -mmacosx-version-min=$MACOS_VERSION" "" "tmp/libfreetype-arm64.a"
echo "Creating universal library using lipo..."
lipo -create -output lib/libfreetype.a tmp/libfreetype-x86_64.a tmp/libfreetype-arm64.a
if [ $? -ne 0 ]; then
echo "Failed to create universal library with lipo"
exit 1
fi
echo "Universal library created at lib/libfreetype.a"
;;
*)
echo "Unknown vendor type: $VTYPE"
exit 1
;;
esac
echo "Script completed successfully."
@@ -10,6 +10,18 @@ import org.gradle.api.tasks.TaskAction
@CompileStatic
class GenerateLibs extends DefaultTask {
private static final String[] INCLUDES = [
'include/imgui',
'include/imnodes',
'include/imgui-node-editor',
'include/imguizmo',
'include/implot',
'include/ImGuiColorTextEdit',
'include/ImGuiFileDialog',
'include/imgui_club/imgui_memory_editor',
'include/imgui-knobs'
]
@Internal
String group = 'build'
@Internal
@@ -26,9 +38,10 @@ class GenerateLibs extends DefaultTask {
private final String sourceDir = project.file('src/main/java')
private final String classpath = project.file('build/classes/java/main')
private final String jniDir = (isLocal ? project.buildDir.path : '/tmp/imgui') + '/jni'
private final String tmpFolder = (isLocal ? project.buildDir.path : '/tmp/imgui') + '/tmp'
private final String libsFolder = 'libsNative'
private final String rootDir = (isLocal ? project.buildDir.path : '/tmp/imgui')
private final String jniDir = "$rootDir/jni"
private final String tmpDir = "$rootDir/tmp"
private final String libsDirName = 'libsNative'
@TaskAction
void generate() {
@@ -42,47 +55,46 @@ class GenerateLibs extends DefaultTask {
throw new IllegalStateException('No build targets')
}
new File(jniDir).deleteDir()
new File(tmpDir).deleteDir()
new File("$rootDir/$libsDirName").deleteDir()
// Generate h/cpp files for JNI
new NativeCodeGenerator().generate(sourceDir, classpath, jniDir)
// Copy ImGui h/cpp files
project.copy { CopySpec spec ->
['include/imgui', 'include/imnodes', 'include/imgui-node-editor',
'include/imguizmo', 'include/implot', 'include/ImGuiColorTextEdit',
'include/ImGuiFileDialog', 'include/imgui_club/imgui_memory_editor', 'include/imgui-knobs'].each {
INCLUDES.each {
spec.from(project.rootProject.file(it)) { CopySpec s -> s.include('*.h', '*.cpp', '*.inl') }
}
spec.from(project.rootProject.file('imgui-binding/src/main/native'))
spec.into(jniDir)
spec.duplicatesStrategy = DuplicatesStrategy.INCLUDE //Allows for duplicate imconfig.h, we ensure the correct one is copied below
spec.duplicatesStrategy = DuplicatesStrategy.INCLUDE // Allows for duplicate imconfig.h, we ensure the correct one is copied below
}
//Ensure we overwrite imconfig.h with our own
// Ensure we overwrite imconfig.h with our own
project.copy { CopySpec spec ->
spec.from(project.rootProject.file('imgui-binding/src/main/native/imconfig.h'))
spec.into(jniDir)
}
//Copy dirent for ImGuiFileDialog
project.copy { CopySpec spec ->
['include/ImGuiFileDialog/dirent'].each {
spec.from(project.rootProject.file(it)) { CopySpec s -> s.include('*.h', '*.cpp', '*.inl') }
}
spec.into(jniDir + '/dirent')
}
if (withFreeType) {
project.copy { CopySpec spec ->
spec.from(project.rootProject.file('include/imgui/misc/freetype')) { CopySpec it -> it.include('*.h', '*.cpp') }
spec.into("$jniDir/misc/freetype")
}
enableDefine('IMGUI_ENABLE_FREETYPE')
}
// Copy dirent for ImGuiFileDialog
project.copy { CopySpec spec ->
spec.from(project.rootProject.file('include/ImGuiFileDialog/dirent')) { CopySpec s -> s.include('*.h', '*.cpp', '*.inl') }
spec.into(jniDir + '/dirent')
}
// Generate platform dependant ant configs and header files
def buildConfig = new BuildConfig('imgui-java', tmpFolder, libsFolder, jniDir)
def buildTargets = [] as BuildTarget[]
def buildConfig = new BuildConfig('imgui-java', tmpDir, libsDirName, jniDir)
BuildTarget[] buildTargets = []
if (forWindows) {
def win64 = BuildTarget.newDefaultTarget(BuildTarget.TargetOs.Windows, true)
@@ -97,23 +109,11 @@ class GenerateLibs extends DefaultTask {
}
if (forMac) {
def minMacOsVersion = '10.15'
def mac64 = BuildTarget.newDefaultTarget(BuildTarget.TargetOs.MacOsX, true)
mac64.cppFlags += ' -std=c++14'
mac64.cppFlags = mac64.cppFlags.replace('10.7', minMacOsVersion)
mac64.linkerFlags = mac64.linkerFlags.replace('10.7', minMacOsVersion)
addFreeTypeIfEnabled(mac64)
buildTargets += mac64
buildTargets += createMacTarget(false)
}
if (forMacArm64) {
def minMacOsVersion = '10.15'
def macArm64 = BuildTarget.newDefaultTarget(BuildTarget.TargetOs.MacOsX, true, true)
macArm64.cppFlags += ' -std=c++14'
macArm64.cppFlags = macArm64.cppFlags.replace('10.7', minMacOsVersion)
macArm64.linkerFlags = macArm64.linkerFlags.replace('10.7', minMacOsVersion)
addFreeTypeIfEnabled(macArm64)
buildTargets += macArm64
buildTargets += createMacTarget(true)
}
new AntScriptGenerator().generate(buildConfig, buildTargets)
@@ -133,6 +133,34 @@ class GenerateLibs extends DefaultTask {
BuildExecutor.executeAnt(jniDir + '/build-macosxarm64.xml', commonParams)
BuildExecutor.executeAnt(jniDir + '/build.xml', '-v', 'pack-natives')
if (forWindows)
checkLibExist("windows64/imgui-java64.dll")
if (forLinux)
checkLibExist("linux64/libimgui-java64.so")
if (forMac)
checkLibExist("macosx64/libimgui-java64.dylib")
if (forMacArm64)
checkLibExist("macosxarm64/libimgui-java64.dylib")
}
void checkLibExist(String libName) {
def path = new File("$rootDir/$libsDirName/$libName")
if (!path.exists()) {
logger.error("Failed to build $libName!")
throw new IllegalStateException("$path does not exist")
}
}
BuildTarget createMacTarget(Boolean isArm) {
def minMacOsVersion = '10.15'
def macTarget = BuildTarget.newDefaultTarget(BuildTarget.TargetOs.MacOsX, true, isArm)
macTarget.libName = "libimgui-java64.dylib" // Lib for arm64 will be named the same for consistency.
macTarget.cppFlags += ' -std=c++14'
macTarget.cppFlags = macTarget.cppFlags.replace('10.7', minMacOsVersion)
macTarget.linkerFlags = macTarget.linkerFlags.replace('10.7', minMacOsVersion)
addFreeTypeIfEnabled(macTarget)
return macTarget
}
void addFreeTypeIfEnabled(BuildTarget target) {
@@ -140,22 +168,18 @@ class GenerateLibs extends DefaultTask {
return
}
switch (target.os) {
case BuildTarget.TargetOs.Windows:
target.cppFlags += ' -I/usr/x86_64-w64-mingw32/include/freetype2'
break
case BuildTarget.TargetOs.Linux:
target.cppFlags += ' -I/usr/include/freetype2'
break
case BuildTarget.TargetOs.MacOsX:
target.cppFlags += ' -I/usr/local/include/freetype2'
break
def freetypeVendorDir = project.rootProject.file('build/vendor/freetype')
if (!freetypeVendorDir.exists()) {
logger.error("$freetypeVendorDir doesn't exist! Run 'buildSrc/scripts/vendor_freetype.sh' for your platform beforehand!")
throw new IllegalStateException("Unable to build library for FreeType")
}
target.cppFlags += " -I$freetypeVendorDir/include"
target.linkerFlags += " -L${project.rootProject.file("$freetypeVendorDir/lib")}"
target.libraries += ' -lfreetype'
}
void enableDefine(String define) {
project.file("$jniDir/imconfig.h").text += "#define $define"
new File("$jniDir/imconfig.h").text += "#define $define"
}
}