From 16b62d7a326d566f2adaf39c57308792c84accc1 Mon Sep 17 00:00:00 2001 From: xingluo233 Date: Wed, 25 Feb 2026 13:51:43 +0800 Subject: [PATCH] Add Android target support and upgrade Zig to 0.15.2 --- .github/workflows/zig-embedded.yml | 53 ++++++++-- README.md | 2 +- build.zig | 161 +++++++++++++++++++++++++++-- build.zig.zon | 2 +- test_zigpkg/build.zig | 3 +- 5 files changed, 200 insertions(+), 21 deletions(-) diff --git a/.github/workflows/zig-embedded.yml b/.github/workflows/zig-embedded.yml index 74e46bb6c3083..954607b7bdfa3 100644 --- a/.github/workflows/zig-embedded.yml +++ b/.github/workflows/zig-embedded.yml @@ -7,22 +7,53 @@ jobs: strategy: fail-fast: false matrix: - targets: - - aarch64-linux-gnu - - aarch64-linux - - riscv64-linux-musl - - powerpc64-linux - - aarch64-windows + include: + - target: aarch64-linux-gnu + - target: aarch64-linux + - target: riscv64-linux-musl + - target: powerpc64-linux + - target: aarch64-windows + # Android targets (requires NDK) + - target: aarch64-linux-android + android_api: "28" + - target: arm-linux-androideabi + android_api: "28" + - target: x86_64-linux-android + android_api: "28" + - target: x86-linux-android + android_api: "28" + - target: riscv64-linux-android + android_api: "35" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - uses: mlugg/setup-zig@v2 # with: # version: master - - name: Build Summary ${{ matrix.targets }} - run: zig build --summary all -freference-trace -Dtarget=${{ matrix.targets }} - - name: Build Summary - Zig Package ${{ matrix.targets }} - run: zig build --build-file $PWD/test_zigpkg/build.zig --summary all -freference-trace -Dtarget=${{ matrix.targets }} + + - name: Setup Android NDK + if: contains(matrix.target, 'android') + id: setup-ndk + uses: nttld/setup-ndk@v1 + with: + ndk-version: r29 + local-cache: true + + - name: Build Summary ${{ matrix.target }} + env: + ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} + run: | + ARGS="-Dtarget=${{ matrix.target }}" + if [ -n "${{ matrix.android_api }}" ]; then + ARGS="$ARGS -Dandroid-api-level=${{ matrix.android_api }}" + fi + zig build --summary all -freference-trace $ARGS + - name: Build Summary - Zig Package ${{ matrix.target }} + env: + ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} + run: | + ARGS="-Dtarget=${{ matrix.target }}" + zig build --build-file $PWD/test_zigpkg/build.zig --summary all -freference-trace $ARGS diff --git a/README.md b/README.md index 3064cf3f6c153..fd6163aa3c7ae 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Welcome to the OpenSSL Project (Forked to zig-pkg support [v0.14.0 <-> 0.15.1 compat]) +Welcome to the OpenSSL Project (Forked to zig-pkg support v0.15.2) ============================== [![openssl logo]][www.openssl.org] diff --git a/build.zig b/build.zig index 0dbac7b2c123d..f4f44dd1d34d1 100644 --- a/build.zig +++ b/build.zig @@ -1,11 +1,14 @@ const std = @import("std"); +const builtin = @import("builtin"); -pub fn build(b: *std.Build) void { +pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); - const crypto = libcrypto(b, target, optimize); - const ssl = libssl(b, target, optimize); + const android_api_level = b.option([]const u8, "android-api-level", "Android API Level (default: 29)") orelse "29"; + + const crypto = try libcrypto(b, target, optimize, android_api_level); + const ssl = try libssl(b, target, optimize, android_api_level); crypto.installHeadersDirectory(b.path("include/crypto"), "crypto", .{}); crypto.installHeadersDirectory(b.path("include/internal"), "internal", .{}); @@ -15,7 +18,7 @@ pub fn build(b: *std.Build) void { b.installArtifact(ssl); } -fn libcrypto(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { +fn libcrypto(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, android_api_level: []const u8) !*std.Build.Step.Compile { const lib = b.addLibrary(.{ .name = "crypto", .root_module = b.createModule(.{ @@ -28,6 +31,11 @@ fn libcrypto(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.buil .Debug, .ReleaseSafe => lib.bundle_compiler_rt = true, else => lib.root_module.strip = true, } + + if (target.result.abi == .android or target.result.abi == .androideabi) { + try ConfigureAndroidEnvironment(b, lib, target, android_api_level); + } + lib.root_module.addIncludePath(b.path("include")); lib.root_module.addIncludePath(b.path(".")); lib.root_module.addIncludePath(b.path("providers/common/include")); @@ -889,12 +897,12 @@ fn libcrypto(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.buil }, .flags = cflags, }); - lib.root_module.linkLibrary(libprovider(b, target, optimize)); + lib.root_module.linkLibrary(try libprovider(b, target, optimize, android_api_level)); lib.linkLibC(); return lib; } -fn libssl(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { +fn libssl(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, android_api_level: []const u8) !*std.Build.Step.Compile { const lib = b.addLibrary(.{ .name = "ssl", .root_module = b.createModule(.{ @@ -907,6 +915,11 @@ fn libssl(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin .Debug, .ReleaseSafe => lib.bundle_compiler_rt = true, else => lib.root_module.strip = true, } + + if (target.result.abi == .android or target.result.abi == .androideabi) { + try ConfigureAndroidEnvironment(b, lib, target, android_api_level); + } + lib.root_module.addIncludePath(b.path("include")); lib.root_module.addIncludePath(b.path("include_gen")); lib.root_module.addCMacro("OPENSSL_NO_DEPRECATED", ""); @@ -1009,7 +1022,7 @@ fn libssl(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin return lib; } -fn libprovider(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Compile { +fn libprovider(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, android_api_level: []const u8) !*std.Build.Step.Compile { const lib = b.addLibrary(.{ .name = "providers", .root_module = b.createModule(.{ @@ -1017,6 +1030,11 @@ fn libprovider(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.bu .optimize = optimize, }), }); + + if (target.result.abi == .android or target.result.abi == .androideabi) { + try ConfigureAndroidEnvironment(b, lib, target, android_api_level); + } + lib.root_module.addIncludePath(b.path("include")); lib.root_module.addIncludePath(b.path(".")); lib.root_module.addIncludePath(b.path("providers/common/include")); @@ -1280,3 +1298,132 @@ const BuildInfo = struct { return split.first(); } }; + +const AndroidConfigError = error{ + MissingNdkEnvironment, + InvalidNdkPathEncoding, + UnsupportedHostOperatingSystem, + UnsupportedHostArchitecture, + UnsupportedTargetArchitecture, + InvalidApiLevel, + NdkPathNotFound, + SysrootNotFound, + LibCRenderFailed, + OutOfMemory, +}; + +fn ConfigureAndroidEnvironment(b: *std.Build, compile: *std.Build.Step.Compile, target: std.Build.ResolvedTarget, api_level: []const u8) AndroidConfigError!void { + const api_level_int = std.fmt.parseInt(u32, api_level, 10) catch { + std.log.err("Invalid API level format: '{s}', expected numeric string (e.g., '21')", .{api_level}); + return AndroidConfigError.InvalidApiLevel; + }; + + const cpu_arch = target.result.cpu.arch; + + if (cpu_arch == .riscv64) { + if (api_level_int < 35) { + std.log.err("RISC-V 64 Android target requires API level 35 or higher (requested: {d}).", .{api_level_int}); + return AndroidConfigError.InvalidApiLevel; + } + } + + if (api_level_int < 24) { + std.log.warn("API level {d} is very low, modern NDKs may not support it.", .{api_level_int}); + } + + const NDK_ENV_VAR = "ANDROID_NDK_HOME"; + + const ndk_home = std.process.getEnvVarOwned(b.allocator, NDK_ENV_VAR) catch |err| switch (err) { + error.EnvironmentVariableNotFound => { + std.log.err("Environment variable '{s}' not found", .{NDK_ENV_VAR}); + return AndroidConfigError.MissingNdkEnvironment; + }, + error.InvalidWtf8 => { + std.log.err("Environment variable '{s}' contains invalid UTF-8 encoding", .{NDK_ENV_VAR}); + return AndroidConfigError.InvalidNdkPathEncoding; + }, + error.OutOfMemory => return AndroidConfigError.OutOfMemory, + }; + defer b.allocator.free(ndk_home); + + std.fs.accessAbsolute(ndk_home, .{}) catch { + std.log.err("NDK path not accessible: {s}", .{ndk_home}); + return AndroidConfigError.NdkPathNotFound; + }; + + const host_os = builtin.os.tag; + const host_arch = builtin.cpu.arch; + const os_name = switch (host_os) { + .windows => "windows", + .linux => "linux", + .macos => "darwin", + else => { + std.log.err("Unsupported host OS: {s}", .{@tagName(host_os)}); + return AndroidConfigError.UnsupportedHostOperatingSystem; + }, + }; + const arch_name = switch (host_arch) { + .x86_64 => "x86_64", + .aarch64 => "aarch64", + .x86 => "x86", + else => { + std.log.err("Unsupported host arch: {s}", .{@tagName(host_arch)}); + return AndroidConfigError.UnsupportedHostArchitecture; + }, + }; + const host_tag = std.fmt.allocPrint(b.allocator, "{s}-{s}", .{ os_name, arch_name }) catch { + return AndroidConfigError.OutOfMemory; + }; + defer b.allocator.free(host_tag); + + const target_arch = switch (cpu_arch) { + .aarch64 => "aarch64-linux-android", + .x86_64 => "x86_64-linux-android", + .arm, .thumb => "arm-linux-androideabi", + .x86 => "i686-linux-android", + .riscv64 => "riscv64-linux-android", + else => { + std.log.err("Unsupported target arch: {s}", .{@tagName(cpu_arch)}); + return AndroidConfigError.UnsupportedTargetArchitecture; + }, + }; + + const sysroot_path = std.fs.path.join(b.allocator, &.{ ndk_home, "toolchains", "llvm", "prebuilt", host_tag, "sysroot" }) catch return AndroidConfigError.OutOfMemory; + defer b.allocator.free(sysroot_path); + + std.fs.accessAbsolute(sysroot_path, .{}) catch { + std.log.err("Sysroot not found at: {s}", .{sysroot_path}); + std.log.err("Check if your NDK version matches the expected directory structure.", .{}); + return AndroidConfigError.SysrootNotFound; + }; + + const include_path = std.fs.path.join(b.allocator, &.{ sysroot_path, "usr", "include" }) catch return AndroidConfigError.OutOfMemory; + defer b.allocator.free(include_path); + + const lib_path = std.fs.path.join(b.allocator, &.{ sysroot_path, "usr", "lib", target_arch, api_level }) catch return AndroidConfigError.OutOfMemory; + defer b.allocator.free(lib_path); + + const system_include_path = std.fs.path.join(b.allocator, &.{ sysroot_path, "usr", "include", target_arch }) catch return AndroidConfigError.OutOfMemory; + defer b.allocator.free(system_include_path); + + compile.root_module.addSystemIncludePath(.{ .cwd_relative = include_path }); + compile.root_module.addSystemIncludePath(.{ .cwd_relative = system_include_path }); + + compile.root_module.addLibraryPath(.{ .cwd_relative = lib_path }); + + var writer = std.Io.Writer.Allocating.init(b.allocator); + + const libc_installation = std.zig.LibCInstallation{ + .include_dir = include_path, + .sys_include_dir = system_include_path, + .crt_dir = lib_path, + }; + + libc_installation.render(&writer.writer) catch { + std.log.err("Failed to render libc installation configuration.", .{}); + return AndroidConfigError.LibCRenderFailed; + }; + const libc_path = b.addWriteFiles().add("android-libc.conf", writer.written()); + + compile.setLibCFile(libc_path); +} diff --git a/build.zig.zon b/build.zig.zon index 2a027d9f7cc3f..60aa16eeeb565 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,7 +1,7 @@ .{ .name = .openssl, .version = "3.6.0", - .minimum_zig_version = "0.14.0", + .minimum_zig_version = "0.15.2", .fingerprint = 0x773a47f1ef0097c1, .paths = .{ "build.zig", diff --git a/test_zigpkg/build.zig b/test_zigpkg/build.zig index 55c274bca46e8..1a19182e6e163 100644 --- a/test_zigpkg/build.zig +++ b/test_zigpkg/build.zig @@ -10,8 +10,9 @@ pub fn build(b: *std.Build) void { .optimize = optimize, }); - const lib = b.addStaticLibrary(.{ + const lib = b.addLibrary(.{ .name = "openssl", + .linkage = .static, .root_module = mod_openssl, });