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
//! A helper crate to retrieve information about the installed swipl environment.
#![doc(html_root_url = "https://terminusdb-labs.github.io/swipl-rs/swipl_info/")]

use regex::*;
use std::env;
use std::process::Command;

/// Struct containing information about a SWI-Prolog installation
#[derive(Debug)]
pub struct SwiplInfo {
    /// The SWI-Prolog version as an integer
    pub version: u32,
    /// The main directory where SWI-Prolog is located
    pub swi_home: String,
    /// The directory subpath where dynamic libraries live
    pub pack_so_dir: String,
    /// The cflags that swipl advises should be used in module compiles
    pub cflags: String,
    /// The ldflags that swipl advises should be used in module compiles
    pub ldflags: String,
    /// The current architecture
    pub arch: String,
    /// The swipl lib name on this platform
    pub lib_name: String,
    /// The directory with the dynamic libraries
    pub lib_dir: String,
    /// The directory with the header files
    pub header_dir: String,
}

/// Retrieve information about the installed swipl environment.
///
/// This will check the SWIPL environment variable for a path to the
/// swipl binary. If this environment variable is not set, it'll
/// attempt to find swipl by assuming it is on the PATH. When found,
/// some prolog is run to extract this information using the
/// `prolog_pack` library, and returned as a `SwiplInfo`.
///
/// If swipl is not found, this function panics.
pub fn get_swipl_info() -> SwiplInfo {
    // by retrieving SWIPL from env, this should work within swipl
    // build environments, as these set this environment variable
    // appropriately.
    let swipl_path = env::var("SWIPL").unwrap_or_else(|_| "swipl".to_string());

    // let's first retrieve a version number
    let output = Command::new(&swipl_path)
        .arg("-g")
        .arg("current_prolog_flag(version, Version), writeq(Version)")
        .arg("-g")
        .arg("halt")
        .output()
        .unwrap();

    if !output.status.success() {
        panic!("Error retrieving version number using swipl: {:?}", output);
    }

    let version: u32 = String::from_utf8_lossy(&output.stdout).parse().unwrap();

    let build_env_command = if version >= 80504 {
        // build_environment predicate moved and is now called somewhat differently
        "use_module(library(build/tools)), build_tools:build_environment(Env, []), memberchk('SWIHOME'=Swihome, Env), memberchk('PACKSODIR'=Packsodir, Env), memberchk('CFLAGS'=Cflags, Env), memberchk('LDSOFLAGS'=Ldflags, Env), format('~s~n~s~n~s~n~s~n', [Swihome, Packsodir, Cflags, Ldflags])"
    } else {
        "use_module(library(prolog_pack)), prolog_pack:build_environment(Env), memberchk('SWIHOME'=Swihome, Env), memberchk('PACKSODIR'=Packsodir, Env), memberchk('CFLAGS'=Cflags, Env), memberchk('LDSOFLAGS'=Ldflags, Env), format('~s~n~s~n~s~n~s~n', [Swihome, Packsodir, Cflags, Ldflags])"
    };

    let output = Command::new(&swipl_path)
        .arg("-g")
        .arg(build_env_command)
        .arg("-g")
        .arg("halt")
        .output()
        .unwrap();

    if !output.status.success() {
        panic!(
            "Error retrieving build environment using swipl: {:?}",
            output
        );
    }

    let runtime_output = Command::new(&swipl_path)
        .arg("--dump-runtime-variables")
        .output()
        .unwrap();

    let runtime_output = String::from_utf8_lossy(&runtime_output.stdout);

    let plarch_regex = Regex::new(r#"PLARCH="([^"]*)""#).unwrap();
    let pllibdir_regex = Regex::new(r#"PLLIBDIR="([^"]*)""#).unwrap();

    let arch = plarch_regex.captures(&runtime_output).unwrap()[1].to_string();
    let lib_dir = pllibdir_regex.captures(&runtime_output).unwrap()[1].to_string();

    let stdout = String::from_utf8_lossy(&output.stdout);
    let mut lines = stdout.lines();
    let swi_home = lines.next().unwrap().to_owned();
    let pack_so_dir = lines.next().unwrap().to_owned();
    let cflags = lines.next().unwrap().to_owned();
    let ldflags = lines.next().unwrap().to_owned();

    let lib_name = if cfg!(target_os = "windows") {
        "libswipl".to_string()
    } else {
        "swipl".to_string()
    };

    let header_dir = format!("{}/include", swi_home);

    SwiplInfo {
        version,
        swi_home,
        pack_so_dir,
        cflags,
        ldflags,
        arch,
        lib_name,
        lib_dir,
        header_dir,
    }
}