1 extern crate cc;
2 extern crate pkg_config;
3 #[cfg(target_env = "msvc")]
4 extern crate vcpkg;
5
6 use std::env;
7 use std::fs;
8 use std::path::{Path, PathBuf};
9 use std::process::Command;
10
main()11 fn main() {
12 let host = env::var("HOST").unwrap();
13 let target = env::var("TARGET").unwrap();
14 let windows = target.contains("windows");
15
16 // This feature trumps all others, and is largely set by rustbuild to force
17 // usage of the system library to ensure that we're always building an
18 // ABI-compatible Cargo.
19 if cfg!(feature = "force-system-lib-on-osx") && target.contains("apple") {
20 return println!("cargo:rustc-flags=-l curl");
21 }
22
23 // When cross-compiling for Haiku, use the system's default supplied
24 // libcurl (it supports http2). This is in the case where rustc and
25 // cargo are built for Haiku, which is done from a Linux host.
26 if host != target && target.contains("haiku") {
27 return println!("cargo:rustc-flags=-l curl");
28 }
29
30 // If the static-curl feature is disabled, probe for a system-wide libcurl.
31 if !cfg!(feature = "static-curl") {
32 // OSX ships libcurl by default, so we just use that version
33 // so long as it has the right features enabled.
34 if target.contains("apple") && (!cfg!(feature = "http2") || curl_config_reports_http2()) {
35 return println!("cargo:rustc-flags=-l curl");
36 }
37
38 // Next, fall back and try to use pkg-config if its available.
39 if windows {
40 if try_vcpkg() {
41 return;
42 }
43 } else if try_pkg_config() {
44 return;
45 }
46 }
47
48 if !Path::new("curl/.git").exists() {
49 let _ = Command::new("git")
50 .args(&["submodule", "update", "--init"])
51 .status();
52 }
53
54 if target.contains("apple") {
55 // On (older) OSX we need to link against the clang runtime,
56 // which is hidden in some non-default path.
57 //
58 // More details at https://github.com/alexcrichton/curl-rust/issues/279.
59 if let Some(path) = macos_link_search_path() {
60 println!("cargo:rustc-link-lib=clang_rt.osx");
61 println!("cargo:rustc-link-search={}", path);
62 }
63 }
64
65 let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
66 let include = dst.join("include");
67 let build = dst.join("build");
68 println!("cargo:root={}", dst.display());
69 println!("cargo:include={}", include.display());
70 println!("cargo:static=1");
71 fs::create_dir_all(include.join("curl")).unwrap();
72
73 for header in [
74 "curl.h",
75 "curlver.h",
76 "easy.h",
77 "options.h",
78 "mprintf.h",
79 "multi.h",
80 "stdcheaders.h",
81 "system.h",
82 "urlapi.h",
83 "typecheck-gcc.h",
84 ]
85 .iter()
86 {
87 fs::copy(
88 format!("curl/include/curl/{}", header),
89 include.join("curl").join(header),
90 )
91 .unwrap();
92 }
93
94 let pkgconfig = dst.join("lib/pkgconfig");
95 fs::create_dir_all(&pkgconfig).unwrap();
96 let contents = fs::read_to_string("curl/libcurl.pc.in").unwrap();
97 fs::write(
98 pkgconfig.join("libcurl.pc"),
99 contents
100 .replace("@prefix@", dst.to_str().unwrap())
101 .replace("@exec_prefix@", "")
102 .replace("@libdir@", dst.join("lib").to_str().unwrap())
103 .replace("@includedir@", include.to_str().unwrap())
104 .replace("@CPPFLAG_CURL_STATICLIB@", "-DCURL_STATICLIB")
105 .replace("@LIBCURL_LIBS@", "")
106 .replace("@SUPPORT_FEATURES@", "")
107 .replace("@SUPPORT_PROTOCOLS@", "")
108 .replace("@CURLVERSION@", "7.61.1"),
109 )
110 .unwrap();
111
112 let mut cfg = cc::Build::new();
113 cfg.out_dir(&build)
114 .include("curl/lib")
115 .include("curl/include")
116 .define("BUILDING_LIBCURL", None)
117 .define("CURL_DISABLE_CRYPTO_AUTH", None)
118 .define("CURL_DISABLE_DICT", None)
119 .define("CURL_DISABLE_GOPHER", None)
120 .define("CURL_DISABLE_IMAP", None)
121 .define("CURL_DISABLE_LDAP", None)
122 .define("CURL_DISABLE_LDAPS", None)
123 .define("CURL_DISABLE_NTLM", None)
124 .define("CURL_DISABLE_POP3", None)
125 .define("CURL_DISABLE_RTSP", None)
126 .define("CURL_DISABLE_SMB", None)
127 .define("CURL_DISABLE_SMTP", None)
128 .define("CURL_DISABLE_TELNET", None)
129 .define("CURL_DISABLE_TFTP", None)
130 .define("CURL_STATICLIB", None)
131 .define("ENABLE_IPV6", None)
132 .define("HAVE_ASSERT_H", None)
133 .define("OS", "\"unknown\"") // TODO
134 .define("HAVE_ZLIB_H", None)
135 .define("HAVE_LIBZ", None)
136 .file("curl/lib/asyn-thread.c")
137 .file("curl/lib/altsvc.c")
138 .file("curl/lib/base64.c")
139 .file("curl/lib/conncache.c")
140 .file("curl/lib/connect.c")
141 .file("curl/lib/content_encoding.c")
142 .file("curl/lib/cookie.c")
143 .file("curl/lib/curl_addrinfo.c")
144 .file("curl/lib/curl_ctype.c")
145 .file("curl/lib/curl_get_line.c")
146 .file("curl/lib/curl_memrchr.c")
147 .file("curl/lib/curl_range.c")
148 .file("curl/lib/curl_threads.c")
149 .file("curl/lib/dotdot.c")
150 .file("curl/lib/doh.c")
151 .file("curl/lib/dynbuf.c")
152 .file("curl/lib/easy.c")
153 .file("curl/lib/escape.c")
154 .file("curl/lib/file.c")
155 .file("curl/lib/fileinfo.c")
156 .file("curl/lib/formdata.c")
157 .file("curl/lib/getenv.c")
158 .file("curl/lib/getinfo.c")
159 .file("curl/lib/hash.c")
160 .file("curl/lib/hostasyn.c")
161 .file("curl/lib/hostcheck.c")
162 .file("curl/lib/hostip.c")
163 .file("curl/lib/hostip6.c")
164 .file("curl/lib/http.c")
165 .file("curl/lib/http2.c")
166 .file("curl/lib/http_chunks.c")
167 .file("curl/lib/http_proxy.c")
168 .file("curl/lib/if2ip.c")
169 .file("curl/lib/inet_ntop.c")
170 .file("curl/lib/inet_pton.c")
171 .file("curl/lib/llist.c")
172 .file("curl/lib/mime.c")
173 .file("curl/lib/mprintf.c")
174 .file("curl/lib/mqtt.c")
175 .file("curl/lib/multi.c")
176 .file("curl/lib/netrc.c")
177 .file("curl/lib/nonblock.c")
178 .file("curl/lib/parsedate.c")
179 .file("curl/lib/progress.c")
180 .file("curl/lib/rand.c")
181 .file("curl/lib/rename.c")
182 .file("curl/lib/select.c")
183 .file("curl/lib/sendf.c")
184 .file("curl/lib/setopt.c")
185 .file("curl/lib/share.c")
186 .file("curl/lib/slist.c")
187 .file("curl/lib/socks.c")
188 .file("curl/lib/socketpair.c")
189 .file("curl/lib/speedcheck.c")
190 .file("curl/lib/splay.c")
191 .file("curl/lib/strcase.c")
192 .file("curl/lib/strdup.c")
193 .file("curl/lib/strerror.c")
194 .file("curl/lib/strtok.c")
195 .file("curl/lib/strtoofft.c")
196 .file("curl/lib/timeval.c")
197 .file("curl/lib/transfer.c")
198 .file("curl/lib/url.c")
199 .file("curl/lib/urlapi.c")
200 .file("curl/lib/version.c")
201 .file("curl/lib/vtls/keylog.c")
202 .file("curl/lib/vtls/vtls.c")
203 .file("curl/lib/warnless.c")
204 .file("curl/lib/wildcard.c")
205 .define("HAVE_GETADDRINFO", None)
206 .define("HAVE_GETPEERNAME", None)
207 .define("HAVE_GETSOCKNAME", None)
208 .warnings(false);
209
210 if cfg!(feature = "protocol-ftp") {
211 cfg.file("curl/lib/curl_fnmatch.c")
212 .file("curl/lib/ftp.c")
213 .file("curl/lib/ftplistparser.c")
214 .file("curl/lib/pingpong.c");
215 } else {
216 cfg.define("CURL_DISABLE_FTP", None);
217 }
218
219 if cfg!(feature = "http2") {
220 cfg.define("USE_NGHTTP2", None)
221 .define("NGHTTP2_STATICLIB", None);
222
223 println!("cargo:rustc-cfg=link_libnghttp2");
224 if let Some(path) = env::var_os("DEP_NGHTTP2_ROOT") {
225 let path = PathBuf::from(path);
226 cfg.include(path.join("include"));
227 }
228 }
229
230 println!("cargo:rustc-cfg=link_libz");
231 if let Some(path) = env::var_os("DEP_Z_INCLUDE") {
232 cfg.include(path);
233 }
234
235 if cfg!(feature = "spnego") {
236 cfg.define("USE_SPNEGO", None)
237 .file("curl/lib/http_negotiate.c")
238 .file("curl/lib/vauth/vauth.c");
239 }
240
241 // Configure TLS backend. Since Cargo does not support mutually exclusive
242 // features, make sure we only compile one vtls.
243 if cfg!(feature = "mesalink") {
244 cfg.define("USE_MESALINK", None)
245 .file("curl/lib/vtls/mesalink.c");
246
247 if let Some(path) = env::var_os("DEP_MESALINK_INCLUDE") {
248 cfg.include(path);
249 }
250
251 if windows {
252 cfg.define("HAVE_WINDOWS", None);
253 } else {
254 cfg.define("HAVE_UNIX", None);
255 }
256 } else if cfg!(feature = "ssl") {
257 if windows {
258 cfg.define("USE_WINDOWS_SSPI", None)
259 .define("USE_SCHANNEL", None)
260 .file("curl/lib/x509asn1.c")
261 .file("curl/lib/curl_sspi.c")
262 .file("curl/lib/socks_sspi.c")
263 .file("curl/lib/vtls/schannel.c")
264 .file("curl/lib/vtls/schannel_verify.c");
265 } else if target.contains("-apple-") {
266 cfg.define("USE_SECTRANSP", None)
267 .file("curl/lib/vtls/sectransp.c");
268 if xcode_major_version().map_or(true, |v| v >= 9) {
269 // On earlier Xcode versions (<9), defining HAVE_BUILTIN_AVAILABLE
270 // would cause __bultin_available() to fail to compile due to
271 // unrecognized platform names, so we try to check for Xcode
272 // version first (if unknown, assume it's recent, as in >= 9).
273 cfg.define("HAVE_BUILTIN_AVAILABLE", "1");
274 }
275 } else {
276 cfg.define("USE_OPENSSL", None)
277 .file("curl/lib/vtls/openssl.c");
278
279 println!("cargo:rustc-cfg=link_openssl");
280 if let Some(path) = env::var_os("DEP_OPENSSL_INCLUDE") {
281 cfg.include(path);
282 }
283 }
284 }
285
286 // Configure platform-specific details.
287 if windows {
288 cfg.define("WIN32", None)
289 .define("USE_THREADS_WIN32", None)
290 .define("HAVE_IOCTLSOCKET_FIONBIO", None)
291 .define("USE_WINSOCK", None)
292 .file("curl/lib/system_win32.c")
293 .file("curl/lib/version_win32.c")
294 .file("curl/lib/curl_multibyte.c");
295
296 if cfg!(feature = "spnego") {
297 cfg.file("curl/lib/vauth/spnego_sspi.c");
298 }
299 } else {
300 cfg.define("RECV_TYPE_ARG1", "int")
301 .define("HAVE_PTHREAD_H", None)
302 .define("HAVE_ARPA_INET_H", None)
303 .define("HAVE_ERRNO_H", None)
304 .define("HAVE_FCNTL_H", None)
305 .define("HAVE_NETDB_H", None)
306 .define("HAVE_NETINET_IN_H", None)
307 .define("HAVE_NETINET_TCP_H", None)
308 .define("HAVE_POLL_FINE", None)
309 .define("HAVE_POLL_H", None)
310 .define("HAVE_FCNTL_O_NONBLOCK", None)
311 .define("HAVE_SYS_SELECT_H", None)
312 .define("HAVE_SYS_STAT_H", None)
313 .define("HAVE_UNISTD_H", None)
314 .define("HAVE_RECV", None)
315 .define("HAVE_SELECT", None)
316 .define("HAVE_SEND", None)
317 .define("HAVE_SOCKET", None)
318 .define("HAVE_STERRROR_R", None)
319 .define("HAVE_SOCKETPAIR", None)
320 .define("HAVE_STRUCT_TIMEVAL", None)
321 .define("HAVE_SYS_UN_H", None)
322 .define("USE_THREADS_POSIX", None)
323 .define("USE_UNIX_SOCKETS", None)
324 .define("RECV_TYPE_ARG2", "void*")
325 .define("RECV_TYPE_ARG3", "size_t")
326 .define("RECV_TYPE_ARG4", "int")
327 .define("RECV_TYPE_RETV", "ssize_t")
328 .define("SEND_QUAL_ARG2", "const")
329 .define("SEND_TYPE_ARG1", "int")
330 .define("SEND_TYPE_ARG2", "void*")
331 .define("SEND_TYPE_ARG3", "size_t")
332 .define("SEND_TYPE_ARG4", "int")
333 .define("SEND_TYPE_RETV", "ssize_t")
334 .define("SIZEOF_CURL_OFF_T", "8")
335 .define("SIZEOF_INT", "4")
336 .define("SIZEOF_SHORT", "2");
337
338 if target.contains("-apple-") {
339 cfg.define("__APPLE__", None)
340 .define("macintosh", None)
341 .define("HAVE_MACH_ABSOLUTE_TIME", None);
342 } else {
343 cfg.define("HAVE_CLOCK_GETTIME_MONOTONIC", None)
344 .define("HAVE_GETTIMEOFDAY", None);
345 }
346
347 if cfg!(feature = "spnego") {
348 cfg.define("HAVE_GSSAPI", None)
349 .file("curl/lib/curl_gssapi.c")
350 .file("curl/lib/socks_gssapi.c")
351 .file("curl/lib/vauth/spnego_gssapi.c");
352 if let Some(path) = env::var_os("GSSAPI_ROOT") {
353 let path = PathBuf::from(path);
354 cfg.include(path.join("include"));
355 }
356
357 // Link against the MIT gssapi library. It might be desirable to add support for
358 // choosing between MIT and Heimdal libraries in the future.
359 println!("cargo:rustc-link-lib=gssapi_krb5");
360 }
361
362 let width = env::var("CARGO_CFG_TARGET_POINTER_WIDTH")
363 .unwrap()
364 .parse::<usize>()
365 .unwrap();
366 cfg.define("SIZEOF_SSIZE_T", Some(&(width / 8).to_string()[..]));
367 cfg.define("SIZEOF_SIZE_T", Some(&(width / 8).to_string()[..]));
368 cfg.define("SIZEOF_LONG", Some(&(width / 8).to_string()[..]));
369
370 cfg.flag("-fvisibility=hidden");
371 }
372
373 cfg.compile("curl");
374
375 if windows {
376 println!("cargo:rustc-link-lib=ws2_32");
377 println!("cargo:rustc-link-lib=crypt32");
378 }
379
380 // Illumos/Solaris requires explicit linking with libnsl
381 if target.contains("solaris") {
382 println!("cargo:rustc-link-lib=nsl");
383 }
384
385 if target.contains("-apple-") {
386 println!("cargo:rustc-link-lib=framework=Security");
387 println!("cargo:rustc-link-lib=framework=CoreFoundation");
388 }
389 }
390
391 #[cfg(not(target_env = "msvc"))]
try_vcpkg() -> bool392 fn try_vcpkg() -> bool {
393 false
394 }
395
396 #[cfg(target_env = "msvc")]
try_vcpkg() -> bool397 fn try_vcpkg() -> bool {
398 // the import library for the dll is called libcurl_imp
399 let mut successful_probe_details = match vcpkg::Config::new()
400 .lib_names("libcurl_imp", "libcurl")
401 .emit_includes(true)
402 .probe("curl")
403 {
404 Ok(details) => Some(details),
405 Err(e) => {
406 println!("first run of vcpkg did not find libcurl: {}", e);
407 None
408 }
409 };
410
411 if successful_probe_details.is_none() {
412 match vcpkg::Config::new()
413 .lib_name("libcurl")
414 .emit_includes(true)
415 .probe("curl")
416 {
417 Ok(details) => successful_probe_details = Some(details),
418 Err(e) => println!("second run of vcpkg did not find libcurl: {}", e),
419 }
420 }
421
422 if successful_probe_details.is_some() {
423 // Found libcurl which depends on openssl, libssh2 and zlib
424 // in the a default vcpkg installation. Probe for them
425 // but do not fail if they are not present as we may be working
426 // with a customized vcpkg installation.
427 vcpkg::Config::new()
428 .lib_name("libeay32")
429 .lib_name("ssleay32")
430 .probe("openssl")
431 .ok();
432
433 vcpkg::probe_package("libssh2").ok();
434
435 vcpkg::Config::new()
436 .lib_names("zlib", "zlib1")
437 .probe("zlib")
438 .ok();
439
440 println!("cargo:rustc-link-lib=crypt32");
441 println!("cargo:rustc-link-lib=gdi32");
442 println!("cargo:rustc-link-lib=user32");
443 println!("cargo:rustc-link-lib=wldap32");
444 return true;
445 }
446 false
447 }
448
try_pkg_config() -> bool449 fn try_pkg_config() -> bool {
450 let mut cfg = pkg_config::Config::new();
451 cfg.cargo_metadata(false);
452 let lib = match cfg.probe("libcurl") {
453 Ok(lib) => lib,
454 Err(e) => {
455 println!(
456 "Couldn't find libcurl from pkgconfig ({:?}), \
457 compiling it from source...",
458 e
459 );
460 return false;
461 }
462 };
463
464 // Not all system builds of libcurl have http2 features enabled, so if we've
465 // got a http2-requested build then we may fall back to a build from source.
466 if cfg!(feature = "http2") && !curl_config_reports_http2() {
467 return false;
468 }
469
470 // Re-find the library to print cargo's metadata, then print some extra
471 // metadata as well.
472 cfg.cargo_metadata(true).probe("libcurl").unwrap();
473 for path in lib.include_paths.iter() {
474 println!("cargo:include={}", path.display());
475 }
476 true
477 }
478
xcode_major_version() -> Option<u8>479 fn xcode_major_version() -> Option<u8> {
480 let output = Command::new("xcodebuild").arg("-version").output().ok()?;
481 if output.status.success() {
482 let stdout = String::from_utf8_lossy(&output.stdout);
483 println!("xcode version: {}", stdout);
484 let mut words = stdout.split_whitespace();
485 if words.next()? == "Xcode" {
486 let version = words.next()?;
487 return version[..version.find('.')?].parse().ok();
488 }
489 }
490 println!("unable to determine Xcode version, assuming >= 9");
491 None
492 }
493
curl_config_reports_http2() -> bool494 fn curl_config_reports_http2() -> bool {
495 let output = Command::new("curl-config").arg("--features").output();
496 let output = match output {
497 Ok(out) => out,
498 Err(e) => {
499 println!("failed to run curl-config ({}), building from source", e);
500 return false;
501 }
502 };
503 if !output.status.success() {
504 println!("curl-config failed: {}", output.status);
505 return false;
506 }
507 let stdout = String::from_utf8_lossy(&output.stdout);
508 if !stdout.contains("HTTP2") {
509 println!(
510 "failed to find http-2 feature enabled in pkg-config-found \
511 libcurl, building from source"
512 );
513 return false;
514 }
515
516 true
517 }
518
macos_link_search_path() -> Option<String>519 fn macos_link_search_path() -> Option<String> {
520 let output = Command::new("clang")
521 .arg("--print-search-dirs")
522 .output()
523 .ok()?;
524 if !output.status.success() {
525 println!(
526 "failed to run 'clang --print-search-dirs', continuing without a link search path"
527 );
528 return None;
529 }
530
531 let stdout = String::from_utf8_lossy(&output.stdout);
532 for line in stdout.lines() {
533 if line.contains("libraries: =") {
534 let path = line.split('=').skip(1).next()?;
535 return Some(format!("{}/lib/darwin", path));
536 }
537 }
538
539 println!("failed to determine link search path, continuing without it");
540 None
541 }
542