1 extern crate cc;
2 extern crate pkg_config;
3 
4 #[cfg(target_env = "msvc")]
5 extern crate vcpkg;
6 
7 use std::env;
8 use std::fs;
9 use std::path::{Path, PathBuf};
10 use std::process::Command;
11 
main()12 fn main() {
13     if try_vcpkg() {
14         return;
15     }
16 
17     // The system copy of libssh2 is not used by default because it
18     // can lead to having two copies of libssl loaded at once.
19     // See https://github.com/alexcrichton/ssh2-rs/pull/88
20     if env::var("LIBSSH2_SYS_USE_PKG_CONFIG").is_ok() {
21         if let Ok(lib) = pkg_config::find_library("libssh2") {
22             for path in &lib.include_paths {
23                 println!("cargo:include={}", path.display());
24             }
25             return;
26         }
27     }
28 
29     if !Path::new("libssh2/.git").exists() {
30         let _ = Command::new("git")
31             .args(&["submodule", "update", "--init"])
32             .status();
33     }
34 
35     let target = env::var("TARGET").unwrap();
36     let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
37     let mut cfg = cc::Build::new();
38 
39     let include = dst.join("include");
40     println!("cargo:include={}", include.display());
41     println!("cargo:root={}", dst.display());
42     let build = dst.join("build");
43     cfg.out_dir(&build);
44     fs::create_dir_all(&build).unwrap();
45     fs::create_dir_all(&include).unwrap();
46 
47     fs::copy("libssh2/include/libssh2.h", include.join("libssh2.h")).unwrap();
48     fs::copy(
49         "libssh2/include/libssh2_publickey.h",
50         include.join("libssh2_publickey.h"),
51     )
52     .unwrap();
53     fs::copy(
54         "libssh2/include/libssh2_sftp.h",
55         include.join("libssh2_sftp.h"),
56     )
57     .unwrap();
58 
59     cfg.file("libssh2/src/agent.c")
60         .file("libssh2/src/bcrypt_pbkdf.c")
61         .file("libssh2/src/blowfish.c")
62         .file("libssh2/src/channel.c")
63         .file("libssh2/src/comp.c")
64         .file("libssh2/src/crypt.c")
65         .file("libssh2/src/global.c")
66         .file("libssh2/src/hostkey.c")
67         .file("libssh2/src/keepalive.c")
68         .file("libssh2/src/kex.c")
69         .file("libssh2/src/knownhost.c")
70         .file("libssh2/src/mac.c")
71         .file("libssh2/src/misc.c")
72         .file("libssh2/src/packet.c")
73         .file("libssh2/src/pem.c")
74         .file("libssh2/src/publickey.c")
75         .file("libssh2/src/scp.c")
76         .file("libssh2/src/session.c")
77         .file("libssh2/src/sftp.c")
78         .file("libssh2/src/transport.c")
79         .file("libssh2/src/userauth.c")
80         .include(&include)
81         .include("libssh2/src");
82 
83     cfg.define("HAVE_LONGLONG", None);
84 
85     if target.contains("windows") {
86         cfg.include("libssh2/win32");
87         cfg.define("LIBSSH2_WINCNG", None);
88         cfg.file("libssh2/src/wincng.c");
89     } else {
90         cfg.flag("-fvisibility=hidden");
91         cfg.define("HAVE_SNPRINTF", None);
92         cfg.define("HAVE_UNISTD_H", None);
93         cfg.define("HAVE_INTTYPES_H", None);
94         cfg.define("HAVE_STDLIB_H", None);
95         cfg.define("HAVE_SYS_SELECT_H", None);
96         cfg.define("HAVE_SYS_SOCKET_H", None);
97         cfg.define("HAVE_SYS_IOCTL_H", None);
98         cfg.define("HAVE_SYS_TIME_H", None);
99         cfg.define("HAVE_SYS_UN_H", None);
100         cfg.define("HAVE_O_NONBLOCK", None);
101         cfg.define("LIBSSH2_OPENSSL", None);
102         cfg.define("HAVE_LIBCRYPT32", None);
103         cfg.define("HAVE_EVP_AES_128_CTR", None);
104         cfg.define("HAVE_POLL", None);
105 
106         cfg.file("libssh2/src/openssl.c");
107 
108         // Create `libssh2_config.h`
109         let config = fs::read_to_string("libssh2/src/libssh2_config_cmake.h.in").unwrap();
110         let config = config
111             .lines()
112             .filter(|l| !l.contains("#cmakedefine"))
113             .collect::<Vec<_>>()
114             .join("\n");
115         fs::write(build.join("libssh2_config.h"), &config).unwrap();
116         cfg.include(&build);
117     }
118 
119     /* Enable newer diffie-hellman-group-exchange-sha1 syntax */
120     cfg.define("LIBSSH2_DH_GEX_NEW", None);
121 
122     cfg.define("LIBSSH2_HAVE_ZLIB", None);
123     if let Some(path) = env::var_os("DEP_Z_INCLUDE") {
124         cfg.include(path);
125     }
126 
127     if let Some(path) = env::var_os("DEP_OPENSSL_INCLUDE") {
128         if let Some(path) = env::split_paths(&path).next() {
129             if let Some(path) = path.to_str() {
130                 if path.len() > 0 {
131                     cfg.include(path);
132                 }
133             }
134         }
135     }
136 
137     let libssh2h = fs::read_to_string("libssh2/include/libssh2.h").unwrap();
138     let version_line = libssh2h
139         .lines()
140         .find(|l| l.contains("LIBSSH2_VERSION"))
141         .unwrap();
142     let version = &version_line[version_line.find('"').unwrap() + 1..version_line.len() - 1];
143 
144     let pkgconfig = dst.join("lib/pkgconfig");
145     fs::create_dir_all(&pkgconfig).unwrap();
146     fs::write(
147         pkgconfig.join("libssh2.pc"),
148         fs::read_to_string("libssh2/libssh2.pc.in")
149             .unwrap()
150             .replace("@prefix@", dst.to_str().unwrap())
151             .replace("@exec_prefix@", "")
152             .replace("@libdir@", dst.join("lib").to_str().unwrap())
153             .replace("@includedir@", include.to_str().unwrap())
154             .replace("@LIBS@", "")
155             .replace("@LIBSREQUIRED@", "")
156             .replace("@LIBSSH2VER@", version),
157     )
158     .unwrap();
159 
160     cfg.warnings(false);
161     cfg.compile("ssh2");
162 
163     if target.contains("windows") {
164         println!("cargo:rustc-link-lib=bcrypt");
165         println!("cargo:rustc-link-lib=crypt32");
166         println!("cargo:rustc-link-lib=user32");
167         println!("cargo:rustc-link-lib=ntdll");
168     }
169 }
170 
171 #[cfg(not(target_env = "msvc"))]
try_vcpkg() -> bool172 fn try_vcpkg() -> bool {
173     false
174 }
175 
176 #[cfg(target_env = "msvc")]
try_vcpkg() -> bool177 fn try_vcpkg() -> bool {
178     vcpkg::Config::new()
179         .emit_includes(true)
180         .probe("libssh2")
181         .map(|_| {
182             // found libssh2 which depends on openssl and zlib
183             vcpkg::Config::new()
184                 .lib_name("libeay32")
185                 .lib_name("ssleay32")
186                 .probe("openssl")
187                 .expect(
188                     "configured libssh2 from vcpkg but could not \
189                      find openssl libraries that it depends on",
190                 );
191 
192             vcpkg::Config::new()
193                 .lib_names("zlib", "zlib1")
194                 .probe("zlib")
195                 .expect(
196                     "configured libssh2 from vcpkg but could not \
197                      find the zlib library that it depends on",
198                 );
199 
200             println!("cargo:rustc-link-lib=crypt32");
201             println!("cargo:rustc-link-lib=gdi32");
202             println!("cargo:rustc-link-lib=user32");
203         })
204         .is_ok()
205 }
206