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