libarchive2-sys 0.2.0

Low-level FFI bindings to libarchive v3.8.1
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
use std::env;
use std::path::PathBuf;

fn main() {
    println!("cargo:rerun-if-changed=libarchive/");

    // Build libarchive using CMake
    build_libarchive();

    // Generate Rust bindings
    generate_bindings();
}

fn build_libarchive() {
    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
    let target = env::var("TARGET").unwrap();

    // Build libarchive with CMake, targeting only the static library
    let mut config = cmake::Config::new("libarchive");
    config
        // Build as static library
        .define("BUILD_SHARED_LIBS", "OFF")
        // Disable building tests, examples, and tools
        .define("ENABLE_TEST", "OFF")
        .define("ENABLE_TAR", "OFF")
        .define("ENABLE_CPIO", "OFF")
        .define("ENABLE_CAT", "OFF")
        .define("ENABLE_UNZIP", "OFF")
        // Disable optional features that might cause issues
        .define("ENABLE_WERROR", "OFF");

    // Android-specific CMake configuration
    if target.contains("android") {
        // Get Android NDK path
        let ndk_home = env::var("ANDROID_NDK_HOME")
            .or_else(|_| env::var("NDK_HOME"))
            .or_else(|_| env::var("ANDROID_NDK"))
            .expect("ANDROID_NDK_HOME environment variable must be set for Android builds");

        let ndk_path = PathBuf::from(&ndk_home);

        // Determine Android ABI and API level
        let android_abi = if target.contains("aarch64") {
            "arm64-v8a"
        } else if target.contains("armv7") {
            "armeabi-v7a"
        } else if target.contains("i686") {
            "x86"
        } else if target.contains("x86_64") {
            "x86_64"
        } else {
            "arm64-v8a"
        };

        // Use Android NDK toolchain file
        let toolchain_file = ndk_path.join("build/cmake/android.toolchain.cmake");

        if toolchain_file.exists() {
            // Set environment variables to point to NDK compilers to avoid "tool not found" warnings
            let ndk_bin = ndk_path.join("toolchains/llvm/prebuilt/darwin-x86_64/bin");
            let arch_prefix = if target.contains("armv7") {
                "armv7a-linux-androideabi"
            } else if target.contains("aarch64") {
                "aarch64-linux-android"
            } else if target.contains("i686") {
                "i686-linux-android"
            } else if target.contains("x86_64") {
                "x86_64-linux-android"
            } else {
                "aarch64-linux-android"
            };

            let c_compiler = ndk_bin.join(format!("{}21-clang", arch_prefix));
            let cpp_compiler = ndk_bin.join(format!("{}21-clang++", arch_prefix));

            // Set environment variables that cc crate respects to avoid detection warnings
            if c_compiler.exists() {
                // SAFETY: Setting environment variables during build script execution is safe
                // as we're in single-threaded build script context
                unsafe {
                    env::set_var(format!("CC_{}", target.replace("-", "_")), &c_compiler);
                }
            }
            if cpp_compiler.exists() {
                // SAFETY: Setting environment variables during build script execution is safe
                // as we're in single-threaded build script context
                unsafe {
                    env::set_var(format!("CXX_{}", target.replace("-", "_")), &cpp_compiler);
                }
            }

            // Use init_c_cfg() to prevent the cmake crate from detecting the host OS
            config.init_c_cfg(cc::Build::new());

            config.define("CMAKE_TOOLCHAIN_FILE", toolchain_file.to_str().unwrap());
            config.define("ANDROID_ABI", android_abi);
            config.define("ANDROID_PLATFORM", "android-21"); // Minimum API 21
            config.define("ANDROID_NDK", &ndk_home);
            config.define("CMAKE_SYSTEM_NAME", "Android");
        }

        // Android - enable all features like other platforms
        config
            .define("ENABLE_ACL", "ON")
            .define("ENABLE_XATTR", "ON")
            .define("ENABLE_ZLIB", "ON")
            .define("ENABLE_BZip2", "ON")
            .define("ENABLE_LZMA", "ON")
            .define("ENABLE_ZSTD", "ON")
            .define("ENABLE_LZ4", "ON");
    } else if target.contains("linux") && !cfg!(target_os = "linux") {
        // Cross-compiling to Linux from another platform

        // Set environment variables to point to Linux cross-compiler to avoid "tool not found" warnings
        let linux_gcc_names = vec!["x86_64-unknown-linux-gnu-gcc", "x86_64-linux-gnu-gcc"];

        for gcc_name in &linux_gcc_names {
            if let Ok(gcc_path) = which::which(gcc_name) {
                let gxx_name = gcc_name.replace("gcc", "g++");
                if let Ok(gxx_path) = which::which(&gxx_name) {
                    // SAFETY: Setting environment variables during build script execution is safe
                    // as we're in single-threaded build script context
                    unsafe {
                        env::set_var(format!("CC_{}", target.replace("-", "_")), &gcc_path);
                        env::set_var(format!("CXX_{}", target.replace("-", "_")), &gxx_path);
                    }
                    break;
                }
            }
        }

        // Use init_c_cfg() to prevent the cmake crate from using wrong compiler names
        config.init_c_cfg(cc::Build::new());

        // Full features for Linux
        config
            .define("ENABLE_ACL", "ON")
            .define("ENABLE_XATTR", "ON")
            .define("ENABLE_ZLIB", "ON")
            .define("ENABLE_BZip2", "ON")
            .define("ENABLE_LZMA", "ON")
            .define("ENABLE_ZSTD", "ON")
            .define("ENABLE_LZ4", "ON");
    } else if target.contains("windows") && !cfg!(target_os = "windows") {
        // Cross-compiling to Windows from another platform

        // Set environment variables to point to MinGW cross-compiler to avoid "tool not found" warnings
        if let Ok(mingw_gcc) = which::which("x86_64-w64-mingw32-gcc")
            && let Ok(mingw_gxx) = which::which("x86_64-w64-mingw32-g++")
        {
            // SAFETY: Setting environment variables during build script execution is safe
            // as we're in single-threaded build script context
            unsafe {
                env::set_var(format!("CC_{}", target.replace("-", "_")), &mingw_gcc);
                env::set_var(format!("CXX_{}", target.replace("-", "_")), &mingw_gxx);
            }
        }

        // Use init_c_cfg() to prevent the cmake crate from auto-detecting wrong platform
        config.init_c_cfg(cc::Build::new());

        // Full features for Windows
        config
            .define("ENABLE_ACL", "ON")
            .define("ENABLE_XATTR", "ON")
            .define("ENABLE_ZLIB", "ON")
            .define("ENABLE_BZip2", "ON")
            .define("ENABLE_LZMA", "ON")
            .define("ENABLE_ZSTD", "ON")
            .define("ENABLE_LZ4", "ON");
    } else if target.contains("wasm") {
        // WASM is not fully supported by libarchive v3.8.1
        // The library requires POSIX types (pid_t, uid_t, gid_t, etc.) that don't exist in WASM
        // CMake configuration fails when trying to detect these types
        panic!(
            "WASM target is not supported by libarchive v3.8.1. \
                The library requires POSIX types and system calls that are not available in WebAssembly. \
                Supported platforms: macOS, Windows, Linux, iOS, Android"
        );
    } else if target.contains("windows") && cfg!(target_os = "windows") {
        // Native Windows build (not cross-compiling)
        // Use vcpkg toolchain file if available
        if let Ok(vcpkg_root) = env::var("VCPKG_INSTALLATION_ROOT") {
            let toolchain_file =
                PathBuf::from(&vcpkg_root).join("scripts/buildsystems/vcpkg.cmake");
            if toolchain_file.exists() {
                config.define("CMAKE_TOOLCHAIN_FILE", toolchain_file.to_str().unwrap());
            }
        }

        // Full features for Windows
        config
            .define("ENABLE_ACL", "ON")
            .define("ENABLE_XATTR", "ON")
            .define("ENABLE_ZLIB", "ON")
            .define("ENABLE_BZip2", "ON")
            .define("ENABLE_LZMA", "ON")
            .define("ENABLE_ZSTD", "ON")
            .define("ENABLE_LZ4", "ON")
            .define("ENABLE_OPENSSL", "ON");
    } else {
        // Full features for native platforms (macOS native, iOS, etc.)
        config
            .define("ENABLE_ACL", "ON")
            .define("ENABLE_XATTR", "ON")
            .define("ENABLE_ZLIB", "ON")
            .define("ENABLE_BZip2", "ON")
            .define("ENABLE_LZMA", "ON")
            .define("ENABLE_ZSTD", "ON")
            .define("ENABLE_LZ4", "ON");
    }

    // Only build the archive_static target to avoid installation issues
    config.build_target("archive_static");

    let _ = config.build();

    // The cmake crate returns the installation directory, but we want the build directory
    // The library is in OUT_DIR/build/libarchive
    let build_dir = out_dir.join("build");

    // On Windows MSVC, the library might be in Debug or Release subdirectory
    if target.contains("windows") && target.contains("msvc") {
        // On Windows MSVC, CMake builds to configuration-specific directories
        // The library file is named "archive.lib" not "archive_static.lib"
        println!(
            "cargo:rustc-link-search=native={}/libarchive/Debug",
            build_dir.display()
        );
        println!(
            "cargo:rustc-link-search=native={}/libarchive/Release",
            build_dir.display()
        );
        println!(
            "cargo:rustc-link-search=native={}/libarchive",
            build_dir.display()
        );
        // The actual library name from CMake is "archive.lib"
        println!("cargo:rustc-link-lib=static=archive");
    } else {
        println!(
            "cargo:rustc-link-search=native={}/libarchive",
            build_dir.display()
        );
        println!("cargo:rustc-link-lib=static=archive");
    }

    // Link system libraries that libarchive depends on
    let target = env::var("TARGET").unwrap();

    if target.contains("apple-darwin") {
        // macOS
        println!("cargo:rustc-link-search=native=/opt/homebrew/lib");
        println!("cargo:rustc-link-search=native=/usr/local/lib");
        println!("cargo:rustc-link-lib=iconv");
        println!("cargo:rustc-link-lib=xml2");
        println!("cargo:rustc-link-lib=b2");
        println!("cargo:rustc-link-lib=framework=CoreFoundation");
        println!("cargo:rustc-link-lib=z");
        println!("cargo:rustc-link-lib=bz2");
        println!("cargo:rustc-link-lib=lzma");
        println!("cargo:rustc-link-lib=zstd");
        println!("cargo:rustc-link-lib=lz4");
    } else if target.contains("apple-ios") {
        // iOS
        println!("cargo:rustc-link-lib=iconv");
        println!("cargo:rustc-link-lib=xml2");
        println!("cargo:rustc-link-lib=framework=CoreFoundation");
        println!("cargo:rustc-link-lib=z");
        println!("cargo:rustc-link-lib=bz2");
        println!("cargo:rustc-link-lib=lzma");
        println!("cargo:rustc-link-lib=zstd");
        println!("cargo:rustc-link-lib=lz4");
    } else if target.contains("linux-android") {
        // Android - link all compression libraries
        println!("cargo:rustc-link-lib=z");
        println!("cargo:rustc-link-lib=bz2");
        println!("cargo:rustc-link-lib=lzma");
        println!("cargo:rustc-link-lib=zstd");
        println!("cargo:rustc-link-lib=lz4");
    } else if target.contains("linux") {
        // Linux
        println!("cargo:rustc-link-lib=pthread");
        println!("cargo:rustc-link-lib=z");
        println!("cargo:rustc-link-lib=bz2");
        println!("cargo:rustc-link-lib=lzma");
        println!("cargo:rustc-link-lib=zstd");
        println!("cargo:rustc-link-lib=lz4");
        // Additional libraries for Linux features
        println!("cargo:rustc-link-lib=xml2"); // For XAR format support
        println!("cargo:rustc-link-lib=crypto"); // For encryption support (OpenSSL)
        println!("cargo:rustc-link-lib=acl"); // For POSIX ACL support
    } else if target.contains("windows") {
        // Windows - library names are different
        if target.contains("msvc") {
            // MSVC toolchain
            // Add vcpkg library path if VCPKG_INSTALLATION_ROOT is set
            if let Ok(vcpkg_root) = env::var("VCPKG_INSTALLATION_ROOT") {
                let vcpkg_lib = PathBuf::from(vcpkg_root)
                    .join("installed")
                    .join("x64-windows")
                    .join("lib");
                if vcpkg_lib.exists() {
                    println!("cargo:rustc-link-search=native={}", vcpkg_lib.display());
                }
            }
            println!("cargo:rustc-link-lib=zlib");
            println!("cargo:rustc-link-lib=bz2");
            println!("cargo:rustc-link-lib=lzma");
            println!("cargo:rustc-link-lib=zstd");
            println!("cargo:rustc-link-lib=lz4");
            println!("cargo:rustc-link-lib=libcrypto"); // OpenSSL libcrypto for digest functions
            println!("cargo:rustc-link-lib=bcrypt"); // For additional crypto functions
            println!("cargo:rustc-link-lib=advapi32");
            println!("cargo:rustc-link-lib=xmllite"); // For XAR format XML parsing
            println!("cargo:rustc-link-lib=ole32"); // For COM stream functions (CreateStreamOnHGlobal, etc.)
        } else {
            // MinGW toolchain
            println!("cargo:rustc-link-lib=z");
            println!("cargo:rustc-link-lib=bz2");
            println!("cargo:rustc-link-lib=lzma");
            println!("cargo:rustc-link-lib=zstd");
            println!("cargo:rustc-link-lib=lz4");
            println!("cargo:rustc-link-lib=crypto"); // OpenSSL libcrypto for digest functions
            println!("cargo:rustc-link-lib=bcrypt");
            println!("cargo:rustc-link-lib=advapi32");
            println!("cargo:rustc-link-lib=xmllite"); // For XAR format XML parsing
            println!("cargo:rustc-link-lib=ole32"); // For COM stream functions
        }
    } else if target.contains("wasm") {
        // WASM - only zlib is enabled, link via emscripten
        println!("cargo:rustc-link-lib=z");
    }
}

fn generate_bindings() {
    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
    let libarchive_include = PathBuf::from("libarchive/libarchive");
    let target = env::var("TARGET").unwrap();

    // Generate bindings
    let mut builder = bindgen::Builder::default()
        .header("wrapper.h")
        .clang_arg(format!("-I{}", libarchive_include.display()))
        // Types to generate
        .allowlist_type("archive.*")
        .allowlist_type("archive_entry.*")
        // Functions to generate
        .allowlist_function("archive.*")
        // Variables to generate
        .allowlist_var("ARCHIVE.*")
        .allowlist_var("AE_.*")
        // Generate constants
        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
        // Formatting
        .derive_debug(true)
        .derive_default(true)
        .size_t_is_usize(true)
        // Disable layout tests to avoid cross-compilation struct size mismatches
        .layout_tests(false);

    // Add platform-specific clang arguments for cross-compilation
    if target.contains("windows") {
        builder = builder
            .clang_arg("--target=x86_64-pc-windows-gnu")
            .clang_arg(format!("-I{}/build", out_dir.display()));

        // Add MinGW sysroot if available
        if let Ok(mingw_gcc) = which::which("x86_64-w64-mingw32-gcc") {
            // Get the real path (resolve symlinks)
            if let Ok(real_path) = std::fs::canonicalize(&mingw_gcc) {
                // Path structure: /opt/homebrew/Cellar/mingw-w64/VERSION/toolchain-x86_64/bin/x86_64-w64-mingw32-gcc
                // We need: /opt/homebrew/Cellar/mingw-w64/VERSION/toolchain-x86_64/x86_64-w64-mingw32/include
                if let Some(bin_dir) = real_path.parent()
                    && let Some(toolchain_dir) = bin_dir.parent()
                {
                    let mingw_include = toolchain_dir.join("x86_64-w64-mingw32/include");
                    if mingw_include.exists() {
                        builder = builder.clang_arg(format!("-I{}", mingw_include.display()));
                    }
                }
            }
        }
    } else if target.contains("android") {
        // Android cross-compilation
        builder = builder.clang_arg(format!("-I{}/build", out_dir.display()));

        // Determine Android architecture and API level
        let ndk_triple = if target.contains("aarch64") {
            "aarch64-linux-android"
        } else if target.contains("armv7") {
            "armv7a-linux-androideabi"
        } else if target.contains("i686") {
            "i686-linux-android"
        } else if target.contains("x86_64") {
            "x86_64-linux-android"
        } else {
            "aarch64-linux-android"
        };

        builder = builder.clang_arg(format!("--target={}", ndk_triple));

        // Try to find Android NDK sysroot
        let ndk_home = env::var("ANDROID_NDK_HOME")
            .or_else(|_| env::var("NDK_HOME"))
            .or_else(|_| env::var("ANDROID_NDK"))
            .ok();

        if let Some(ndk_home) = ndk_home {
            let ndk_path = PathBuf::from(ndk_home);

            // Modern NDK structure (r19+)
            let sysroot = ndk_path.join("toolchains/llvm/prebuilt/darwin-x86_64/sysroot");

            if sysroot.exists() {
                builder = builder
                    .clang_arg(format!("--sysroot={}", sysroot.display()))
                    .clang_arg(format!("-I{}/usr/include", sysroot.display()))
                    .clang_arg(format!(
                        "-I{}/usr/include/{}",
                        sysroot.display(),
                        ndk_triple
                    ));
            }
        }
    } else if target.contains("linux") && !cfg!(target_os = "linux") {
        // Cross-compiling to Linux from another platform
        builder = builder
            .clang_arg("--target=x86_64-unknown-linux-gnu")
            .clang_arg(format!("-I{}/build", out_dir.display()));

        // Try to find Linux cross-compiler sysroot
        let possible_gcc_names = vec!["x86_64-unknown-linux-gnu-gcc", "x86_64-linux-gnu-gcc"];

        for gcc_name in possible_gcc_names {
            if let Ok(linux_gcc) = which::which(gcc_name) {
                // Resolve symlinks to get real path
                // Symlink: /opt/homebrew/Cellar/x86_64-unknown-linux-gnu/VERSION/bin/x86_64-unknown-linux-gnu-gcc
                // Realpath: /opt/homebrew/Cellar/x86_64-unknown-linux-gnu/VERSION/toolchain/bin/x86_64-unknown-linux-gnu-gcc
                // We need: /opt/homebrew/Cellar/x86_64-unknown-linux-gnu/VERSION/toolchain/x86_64-unknown-linux-gnu/sysroot
                if let Ok(real_path) = std::fs::canonicalize(&linux_gcc) {
                    let sysroot = real_path
                        .parent()
                        .and_then(|p| p.parent())
                        .map(|p| p.join("x86_64-unknown-linux-gnu/sysroot"));

                    if let Some(sysroot) = sysroot
                        && sysroot.exists()
                    {
                        builder = builder
                            .clang_arg(format!("--sysroot={}", sysroot.display()))
                            .clang_arg(format!("-I{}/usr/include", sysroot.display()));
                        break;
                    }
                }
            }
        }
    } else if target.contains("wasm") {
        builder = builder.clang_arg("--target=wasm32-unknown-emscripten");
    }

    let bindings = builder.generate().expect("Unable to generate bindings");

    bindings
        .write_to_file(out_dir.join("bindings.rs"))
        .expect("Couldn't write bindings!");
}