1 | //! This crate is the `cpp` cargo build script implementation. It is useless |
2 | //! without the companion crates `cpp`, and `cpp_macro`. |
3 | //! |
4 | //! For more information, see the |
5 | //! [`cpp` crate module level documentation](https://docs.rs/cpp). |
6 | |
7 | #![allow (clippy::write_with_newline)] |
8 | |
9 | mod strnom; |
10 | |
11 | use cpp_common::*; |
12 | use lazy_static::lazy_static; |
13 | use std::collections::hash_map::{Entry, HashMap}; |
14 | use std::env; |
15 | use std::fs::{create_dir, remove_dir_all, File}; |
16 | use std::io::prelude::*; |
17 | use std::path::{Path, PathBuf}; |
18 | |
19 | mod parser; |
20 | |
21 | fn warnln_impl(a: &str) { |
22 | for s: &str in a.lines() { |
23 | println!("cargo:warning= {}" , s); |
24 | } |
25 | } |
26 | |
27 | macro_rules! warnln { |
28 | ($($all:tt)*) => { |
29 | $crate::warnln_impl(&format!($($all)*)); |
30 | } |
31 | } |
32 | |
33 | // Like the write! macro, but add the #line directive (pointing to this file). |
34 | // Note: the string literal must be on on the same line of the macro |
35 | macro_rules! write_add_line { |
36 | ($o:expr, $($e:tt)*) => { |
37 | (|| { |
38 | writeln!($o, "#line {} \"{} \"" , line!(), file!().replace(' \\' , " \\\\" ))?; |
39 | write!($o, $($e)*) |
40 | })() |
41 | }; |
42 | } |
43 | |
44 | const INTERNAL_CPP_STRUCTS: &str = r#" |
45 | /* THIS FILE IS GENERATED BY rust-cpp. DO NOT EDIT */ |
46 | |
47 | #include "stdint.h" // For {u}intN_t |
48 | #include <new> // For placement new |
49 | #include <cstdlib> // For abort |
50 | #include <type_traits> |
51 | #include <utility> |
52 | |
53 | namespace rustcpp { |
54 | |
55 | // We can't just pass or return any type from extern "C" rust functions (because the call |
56 | // convention may differ between the C++ type, and the Rust type). |
57 | // So we make sure to pass trivial structure that only contains a pointer to the object we want to |
58 | // pass. The constructor of these helper class contains a 'container' of the right size which will |
59 | // be allocated on the stack. |
60 | template<typename T> struct return_helper { |
61 | struct container { |
62 | #if defined (_MSC_VER) && (_MSC_VER + 0 < 1900) |
63 | char memory[sizeof(T)]; |
64 | ~container() { reinterpret_cast<T*>(this)->~T(); } |
65 | #else |
66 | // The fact that it is in an union means it is properly sized and aligned, but we have |
67 | // to call the destructor and constructor manually |
68 | union { T memory; }; |
69 | ~container() { memory.~T(); } |
70 | #endif |
71 | container() {} |
72 | }; |
73 | const container* data; |
74 | return_helper(int, const container &c = container()) : data(&c) { } |
75 | }; |
76 | |
77 | template<typename T> struct argument_helper { |
78 | using type = const T&; |
79 | }; |
80 | template<typename T> struct argument_helper<T&> { |
81 | T &ref; |
82 | argument_helper(T &x) : ref(x) {} |
83 | using type = argument_helper<T&> const&; |
84 | }; |
85 | |
86 | template<typename T> |
87 | typename std::enable_if<std::is_copy_constructible<T>::value>::type copy_helper(const void *src, void *dest) |
88 | { new (dest) T (*static_cast<T const*>(src)); } |
89 | template<typename T> |
90 | typename std::enable_if<!std::is_copy_constructible<T>::value>::type copy_helper(const void *, void *) |
91 | { std::abort(); } |
92 | template<typename T> |
93 | typename std::enable_if<std::is_default_constructible<T>::value>::type default_helper(void *dest) |
94 | { new (dest) T(); } |
95 | template<typename T> |
96 | typename std::enable_if<!std::is_default_constructible<T>::value>::type default_helper(void *) |
97 | { std::abort(); } |
98 | |
99 | template<typename T> int compare_helper(const T &a, const T&b, int cmp) { |
100 | switch (cmp) { |
101 | using namespace std::rel_ops; |
102 | case 0: |
103 | if (a < b) |
104 | return -1; |
105 | if (b < a) |
106 | return 1; |
107 | return 0; |
108 | case -2: return a < b; |
109 | case 2: return a > b; |
110 | case -1: return a <= b; |
111 | case 1: return a >= b; |
112 | } |
113 | std::abort(); |
114 | } |
115 | } |
116 | |
117 | #define RUST_CPP_CLASS_HELPER(HASH, ...) \ |
118 | extern "C" { \ |
119 | void __cpp_destructor_##HASH(void *ptr) { typedef __VA_ARGS__ T; static_cast<T*>(ptr)->~T(); } \ |
120 | void __cpp_copy_##HASH(const void *src, void *dest) { rustcpp::copy_helper<__VA_ARGS__>(src, dest); } \ |
121 | void __cpp_default_##HASH(void *dest) { rustcpp::default_helper<__VA_ARGS__>(dest); } \ |
122 | } |
123 | "# ; |
124 | |
125 | lazy_static! { |
126 | static ref CPP_DIR: PathBuf = OUT_DIR.join("rust_cpp" ); |
127 | static ref CARGO_MANIFEST_DIR: PathBuf = PathBuf::from(env::var("CARGO_MANIFEST_DIR" ).expect( |
128 | r#" |
129 | -- rust-cpp fatal error -- |
130 | |
131 | The CARGO_MANIFEST_DIR environment variable was not set. |
132 | NOTE: rust-cpp's build function must be run in a build script."# |
133 | )); |
134 | } |
135 | |
136 | fn gen_cpp_lib(visitor: &parser::Parser) -> PathBuf { |
137 | let result_path = CPP_DIR.join("cpp_closures.cpp" ); |
138 | let mut output = File::create(&result_path).expect("Unable to generate temporary C++ file" ); |
139 | |
140 | write!(output, " {}" , INTERNAL_CPP_STRUCTS).unwrap(); |
141 | |
142 | if visitor.callbacks_count > 0 { |
143 | #[rustfmt::skip] |
144 | write_add_line!(output, r#" |
145 | extern "C" {{ |
146 | void (*rust_cpp_callbacks {file_hash}[ {callbacks_count}])() = {{}}; |
147 | }} |
148 | "# , |
149 | file_hash = *FILE_HASH, |
150 | callbacks_count = visitor.callbacks_count |
151 | ).unwrap(); |
152 | } |
153 | |
154 | write!(output, " {}\n\n" , &visitor.snippets).unwrap(); |
155 | |
156 | let mut hashmap = HashMap::new(); |
157 | |
158 | let mut sizealign = vec![]; |
159 | for Closure { body_str, sig, callback_offset, .. } in &visitor.closures { |
160 | let ClosureSig { captures, cpp, .. } = sig; |
161 | |
162 | let hash = sig.name_hash(); |
163 | let name = sig.extern_name(); |
164 | |
165 | match hashmap.entry(hash) { |
166 | Entry::Occupied(e) => { |
167 | if *e.get() != sig { |
168 | // Let the compiler do a compilation error. FIXME: report a better error |
169 | warnln!("Hash collision detected." ); |
170 | } else { |
171 | continue; |
172 | } |
173 | } |
174 | Entry::Vacant(e) => { |
175 | e.insert(sig); |
176 | } |
177 | } |
178 | |
179 | let is_void = cpp == "void" ; |
180 | |
181 | // Generate the sizes array with the sizes of each of the argument types |
182 | if is_void { |
183 | sizealign.push(format!( |
184 | " {{{hash}ull, 0, 1, {callback_offset}ull << 32 }}" , |
185 | hash = hash, |
186 | callback_offset = callback_offset |
187 | )); |
188 | } else { |
189 | sizealign.push(format!(" {{ |
190 | {hash}ull, |
191 | sizeof( {type}), |
192 | rustcpp::AlignOf< {type}>::value, |
193 | rustcpp::Flags< {type}>::value | {callback_offset}ull << 32 |
194 | }}" , hash=hash, type=cpp, callback_offset = callback_offset)); |
195 | } |
196 | for Capture { cpp, .. } in captures { |
197 | sizealign.push(format!(" {{ |
198 | {hash}ull, |
199 | sizeof( {type}), |
200 | rustcpp::AlignOf< {type}>::value, |
201 | rustcpp::Flags< {type}>::value |
202 | }}" , hash=hash, type=cpp)); |
203 | } |
204 | |
205 | // Generate the parameters and function declaration |
206 | let params = captures |
207 | .iter() |
208 | .map(|&Capture { mutable, ref name, ref cpp }| { |
209 | if mutable { |
210 | format!(" {} & {}" , cpp, name) |
211 | } else { |
212 | format!(" {} const& {}" , cpp, name) |
213 | } |
214 | }) |
215 | .collect::<Vec<_>>() |
216 | .join(", " ); |
217 | |
218 | if is_void { |
219 | #[rustfmt::skip] |
220 | write_add_line!(output, r#" |
221 | extern "C" {{ |
222 | void {name}( {params}) {{ |
223 | {body} |
224 | }} |
225 | }} |
226 | "# , |
227 | name = &name, |
228 | params = params, |
229 | body = body_str |
230 | ).unwrap(); |
231 | } else { |
232 | let comma = if params.is_empty() { "" } else { "," }; |
233 | let args = captures |
234 | .iter() |
235 | .map(|Capture { name, .. }| name.to_string()) |
236 | .collect::<Vec<_>>() |
237 | .join(", " ); |
238 | #[rustfmt::skip] |
239 | write_add_line!(output, r#" |
240 | static inline {ty} {name}_impl( {params}) {{ |
241 | {body} |
242 | }} |
243 | extern "C" {{ |
244 | void {name}( {params}{comma} void* __result) {{ |
245 | ::new(__result) ( {ty})( {name}_impl( {args})); |
246 | }} |
247 | }} |
248 | "# , |
249 | name = &name, |
250 | params = params, |
251 | comma = comma, |
252 | ty = cpp, |
253 | args = args, |
254 | body = body_str |
255 | ).unwrap(); |
256 | } |
257 | } |
258 | |
259 | for class in &visitor.classes { |
260 | let hash = class.name_hash(); |
261 | |
262 | // Generate the sizes array |
263 | sizealign.push(format!(" {{ |
264 | {hash}ull, |
265 | sizeof( {type}), |
266 | rustcpp::AlignOf< {type}>::value, |
267 | rustcpp::Flags< {type}>::value |
268 | }}" , hash=hash, type=class.cpp)); |
269 | |
270 | // Generate helper function. |
271 | // (this is done in a macro, which right after a #line directing pointing to the location of |
272 | // the cpp_class! macro in order to give right line information in the possible errors) |
273 | write!( |
274 | output, |
275 | " {line}RUST_CPP_CLASS_HELPER( {hash}, {cpp_name}) \n" , |
276 | line = class.line, |
277 | hash = hash, |
278 | cpp_name = class.cpp |
279 | ) |
280 | .unwrap(); |
281 | |
282 | if class.derives("PartialEq" ) { |
283 | write!(output, |
284 | " {line}extern \"C \" bool __cpp_equal_ {hash}(const {name} *a, const {name} *b) {{ return *a == *b; }}\n" , |
285 | line = class.line, hash = hash, name = class.cpp).unwrap(); |
286 | } |
287 | if class.derives("PartialOrd" ) { |
288 | write!(output, |
289 | " {line}extern \"C \" bool __cpp_compare_ {hash}(const {name} *a, const {name} *b, int cmp) {{ return rustcpp::compare_helper(*a, *b, cmp); }}\n" , |
290 | line = class.line, hash = hash, name = class.cpp).unwrap(); |
291 | } |
292 | } |
293 | |
294 | let mut magic = vec![]; |
295 | for mag in STRUCT_METADATA_MAGIC.iter() { |
296 | magic.push(format!(" {}" , mag)); |
297 | } |
298 | |
299 | #[rustfmt::skip] |
300 | write_add_line!(output, r#" |
301 | |
302 | namespace rustcpp {{ |
303 | |
304 | template<typename T> |
305 | struct AlignOf {{ |
306 | struct Inner {{ |
307 | char a; |
308 | T b; |
309 | }}; |
310 | static const uintptr_t value = sizeof(Inner) - sizeof(T); |
311 | }}; |
312 | |
313 | template<typename T> |
314 | struct Flags {{ |
315 | static const uintptr_t value = |
316 | (std::is_copy_constructible<T>::value << {flag_is_copy_constructible}) | |
317 | (std::is_default_constructible<T>::value << {flag_is_default_constructible}) | |
318 | #if !defined(__GNUC__) || (__GNUC__ + 0 >= 5) || defined(__clang__) |
319 | (std::is_trivially_destructible<T>::value << {flag_is_trivially_destructible}) | |
320 | (std::is_trivially_copyable<T>::value << {flag_is_trivially_copyable}) | |
321 | (std::is_trivially_default_constructible<T>::value << {flag_is_trivially_default_constructible}) | |
322 | #endif |
323 | 0; |
324 | }}; |
325 | |
326 | struct SizeAlign {{ |
327 | uint64_t hash; |
328 | uint64_t size; |
329 | uint64_t align; |
330 | uint64_t flags; |
331 | }}; |
332 | |
333 | struct MetaData {{ |
334 | uint8_t magic[128]; |
335 | uint8_t version[16]; |
336 | uint64_t endianness_check; |
337 | uint64_t length; |
338 | SizeAlign data[ {length}]; |
339 | }}; |
340 | |
341 | MetaData metadata_ {hash} = {{ |
342 | {{ {magic} }}, |
343 | " {version}", |
344 | 0xffef, |
345 | {length}, |
346 | {{ {data} }} |
347 | }}; |
348 | |
349 | }} // namespace rustcpp |
350 | "# , |
351 | hash = *FILE_HASH, |
352 | data = sizealign.join(", " ), |
353 | length = sizealign.len(), |
354 | magic = magic.join(", " ), |
355 | version = VERSION, |
356 | flag_is_copy_constructible = flags::IS_COPY_CONSTRUCTIBLE, |
357 | flag_is_default_constructible = flags::IS_DEFAULT_CONSTRUCTIBLE, |
358 | flag_is_trivially_destructible = flags::IS_TRIVIALLY_DESTRUCTIBLE, |
359 | flag_is_trivially_copyable = flags::IS_TRIVIALLY_COPYABLE, |
360 | flag_is_trivially_default_constructible = flags::IS_TRIVIALLY_DEFAULT_CONSTRUCTIBLE, |
361 | ).unwrap(); |
362 | |
363 | result_path |
364 | } |
365 | |
366 | fn clean_artifacts() { |
367 | if CPP_DIR.is_dir() { |
368 | remove_dir_all(&*CPP_DIR).expect( |
369 | msg:r#" |
370 | msg:-- rust-cpp fatal error -- |
371 | msg: |
372 | msg:Failed to remove existing build artifacts from output directory."# , |
373 | ); |
374 | } |
375 | |
376 | create_dir(&*CPP_DIR).expect( |
377 | msg:r#" |
378 | msg:-- rust-cpp fatal error -- |
379 | msg: |
380 | msg:Failed to create output object directory."# , |
381 | ); |
382 | } |
383 | |
384 | /// This struct is for advanced users of the build script. It allows providing |
385 | /// configuration options to `cpp` and the compiler when it is used to build. |
386 | /// |
387 | /// ## API Note |
388 | /// |
389 | /// Internally, `cpp` uses the `cc` crate to build the compilation artifact, |
390 | /// and many of the methods defined on this type directly proxy to an internal |
391 | /// `cc::Build` object. |
392 | pub struct Config { |
393 | cc: cc::Build, |
394 | std_flag_set: bool, // true if the -std flag was specified |
395 | } |
396 | |
397 | impl Default for Config { |
398 | fn default() -> Self { |
399 | Config::new() |
400 | } |
401 | } |
402 | |
403 | impl Config { |
404 | /// Create a new `Config` object. This object will hold the configuration |
405 | /// options which control the build. If you don't need to make any changes, |
406 | /// `cpp_build::build` is a wrapper function around this interface. |
407 | pub fn new() -> Config { |
408 | let mut cc = cc::Build::new(); |
409 | cc.cpp(true).include(&*CARGO_MANIFEST_DIR); |
410 | Config { cc, std_flag_set: false } |
411 | } |
412 | |
413 | /// Add a directory to the `-I` or include path for headers |
414 | pub fn include<P: AsRef<Path>>(&mut self, dir: P) -> &mut Self { |
415 | self.cc.include(dir); |
416 | self |
417 | } |
418 | |
419 | /// Specify a `-D` variable with an optional value |
420 | pub fn define(&mut self, var: &str, val: Option<&str>) -> &mut Self { |
421 | self.cc.define(var, val); |
422 | self |
423 | } |
424 | |
425 | // XXX: Make sure that this works with sizes logic |
426 | /// Add an arbitrary object file to link in |
427 | pub fn object<P: AsRef<Path>>(&mut self, obj: P) -> &mut Self { |
428 | self.cc.object(obj); |
429 | self |
430 | } |
431 | |
432 | /// Add an arbitrary flag to the invocation of the compiler |
433 | pub fn flag(&mut self, flag: &str) -> &mut Self { |
434 | if flag.starts_with("-std=" ) { |
435 | self.std_flag_set = true; |
436 | } |
437 | self.cc.flag(flag); |
438 | self |
439 | } |
440 | |
441 | /// Add an arbitrary flag to the invocation of the compiler if it supports it |
442 | pub fn flag_if_supported(&mut self, flag: &str) -> &mut Self { |
443 | if flag.starts_with("-std=" ) { |
444 | self.std_flag_set = true; |
445 | } |
446 | self.cc.flag_if_supported(flag); |
447 | self |
448 | } |
449 | |
450 | // XXX: Make sure this works with sizes logic |
451 | /// Add a file which will be compiled |
452 | pub fn file<P: AsRef<Path>>(&mut self, p: P) -> &mut Self { |
453 | self.cc.file(p); |
454 | self |
455 | } |
456 | |
457 | /// Set the standard library to link against when compiling with C++ |
458 | /// support. |
459 | /// |
460 | /// The default value of this property depends on the current target: On |
461 | /// OS X `Some("c++")` is used, when compiling for a Visual Studio based |
462 | /// target `None` is used and for other targets `Some("stdc++")` is used. |
463 | /// |
464 | /// A value of `None` indicates that no automatic linking should happen, |
465 | /// otherwise cargo will link against the specified library. |
466 | /// |
467 | /// The given library name must not contain the `lib` prefix. |
468 | pub fn cpp_link_stdlib(&mut self, cpp_link_stdlib: Option<&str>) -> &mut Self { |
469 | self.cc.cpp_link_stdlib(cpp_link_stdlib); |
470 | self |
471 | } |
472 | |
473 | /// Force the C++ compiler to use the specified standard library. |
474 | /// |
475 | /// Setting this option will automatically set `cpp_link_stdlib` to the same |
476 | /// value. |
477 | /// |
478 | /// The default value of this option is always `None`. |
479 | /// |
480 | /// This option has no effect when compiling for a Visual Studio based |
481 | /// target. |
482 | /// |
483 | /// This option sets the `-stdlib` flag, which is only supported by some |
484 | /// compilers (clang, icc) but not by others (gcc). The library will not |
485 | /// detect which compiler is used, as such it is the responsibility of the |
486 | /// caller to ensure that this option is only used in conjuction with a |
487 | /// compiler which supports the `-stdlib` flag. |
488 | /// |
489 | /// A value of `None` indicates that no specific C++ standard library should |
490 | /// be used, otherwise `-stdlib` is added to the compile invocation. |
491 | /// |
492 | /// The given library name must not contain the `lib` prefix. |
493 | pub fn cpp_set_stdlib(&mut self, cpp_set_stdlib: Option<&str>) -> &mut Self { |
494 | self.cc.cpp_set_stdlib(cpp_set_stdlib); |
495 | self |
496 | } |
497 | |
498 | // XXX: Add support for custom targets |
499 | // |
500 | // /// Configures the target this configuration will be compiling for. |
501 | // /// |
502 | // /// This option is automatically scraped from the `TARGET` environment |
503 | // /// variable by build scripts, so it's not required to call this function. |
504 | // pub fn target(&mut self, target: &str) -> &mut Self { |
505 | // self.cc.target(target); |
506 | // self |
507 | // } |
508 | |
509 | /// Configures the host assumed by this configuration. |
510 | /// |
511 | /// This option is automatically scraped from the `HOST` environment |
512 | /// variable by build scripts, so it's not required to call this function. |
513 | pub fn host(&mut self, host: &str) -> &mut Self { |
514 | self.cc.host(host); |
515 | self |
516 | } |
517 | |
518 | /// Configures the optimization level of the generated object files. |
519 | /// |
520 | /// This option is automatically scraped from the `OPT_LEVEL` environment |
521 | /// variable by build scripts, so it's not required to call this function. |
522 | pub fn opt_level(&mut self, opt_level: u32) -> &mut Self { |
523 | self.cc.opt_level(opt_level); |
524 | self |
525 | } |
526 | |
527 | /// Configures the optimization level of the generated object files. |
528 | /// |
529 | /// This option is automatically scraped from the `OPT_LEVEL` environment |
530 | /// variable by build scripts, so it's not required to call this function. |
531 | pub fn opt_level_str(&mut self, opt_level: &str) -> &mut Self { |
532 | self.cc.opt_level_str(opt_level); |
533 | self |
534 | } |
535 | |
536 | /// Configures whether the compiler will emit debug information when |
537 | /// generating object files. |
538 | /// |
539 | /// This option is automatically scraped from the `PROFILE` environment |
540 | /// variable by build scripts (only enabled when the profile is "debug"), so |
541 | /// it's not required to call this function. |
542 | pub fn debug(&mut self, debug: bool) -> &mut Self { |
543 | self.cc.debug(debug); |
544 | self |
545 | } |
546 | |
547 | // XXX: Add support for custom out_dir |
548 | // |
549 | // /// Configures the output directory where all object files and static |
550 | // /// libraries will be located. |
551 | // /// |
552 | // /// This option is automatically scraped from the `OUT_DIR` environment |
553 | // /// variable by build scripts, so it's not required to call this function. |
554 | // pub fn out_dir<P: AsRef<Path>>(&mut self, out_dir: P) -> &mut Self { |
555 | // self.cc.out_dir(out_dir); |
556 | // self |
557 | // } |
558 | |
559 | /// Configures the compiler to be used to produce output. |
560 | /// |
561 | /// This option is automatically determined from the target platform or a |
562 | /// number of environment variables, so it's not required to call this |
563 | /// function. |
564 | pub fn compiler<P: AsRef<Path>>(&mut self, compiler: P) -> &mut Self { |
565 | self.cc.compiler(compiler); |
566 | self |
567 | } |
568 | |
569 | /// Configures the tool used to assemble archives. |
570 | /// |
571 | /// This option is automatically determined from the target platform or a |
572 | /// number of environment variables, so it's not required to call this |
573 | /// function. |
574 | pub fn archiver<P: AsRef<Path>>(&mut self, archiver: P) -> &mut Self { |
575 | self.cc.archiver(archiver); |
576 | self |
577 | } |
578 | |
579 | /// Define whether metadata should be emitted for cargo allowing it to |
580 | /// automatically link the binary. Defaults to `true`. |
581 | pub fn cargo_metadata(&mut self, cargo_metadata: bool) -> &mut Self { |
582 | // XXX: Use this to control the cargo metadata which rust-cpp produces |
583 | self.cc.cargo_metadata(cargo_metadata); |
584 | self |
585 | } |
586 | |
587 | /// Configures whether the compiler will emit position independent code. |
588 | /// |
589 | /// This option defaults to `false` for `i686` and `windows-gnu` targets and |
590 | /// to `true` for all other targets. |
591 | pub fn pic(&mut self, pic: bool) -> &mut Self { |
592 | self.cc.pic(pic); |
593 | self |
594 | } |
595 | |
596 | /// Extracts `cpp` declarations from the passed-in crate root, and builds |
597 | /// the associated static library to be linked in to the final binary. |
598 | /// |
599 | /// This method does not perform rust codegen - that is performed by `cpp` |
600 | /// and `cpp_macros`, which perform the actual procedural macro expansion. |
601 | /// |
602 | /// This method may technically be called more than once for ergonomic |
603 | /// reasons, but that usually won't do what you want. Use a different |
604 | /// `Config` object each time you want to build a crate. |
605 | pub fn build<P: AsRef<Path>>(&mut self, crate_root: P) { |
606 | assert_eq!( |
607 | env!("CARGO_PKG_VERSION" ), |
608 | VERSION, |
609 | "Internal Error: mismatched cpp_common and cpp_build versions" |
610 | ); |
611 | |
612 | // Clean up any leftover artifacts |
613 | clean_artifacts(); |
614 | |
615 | // Parse the crate |
616 | let mut visitor = parser::Parser::default(); |
617 | if let Err(err) = visitor.parse_crate(crate_root.as_ref().to_owned()) { |
618 | warnln!( |
619 | r#"-- rust-cpp parse error -- |
620 | There was an error parsing the crate for the rust-cpp build script: |
621 | {} |
622 | In order to provide a better error message, the build script will exit successfully, such that rustc can provide an error message."# , |
623 | err |
624 | ); |
625 | return; |
626 | } |
627 | |
628 | // Generate the C++ library code |
629 | let filename = gen_cpp_lib(&visitor); |
630 | |
631 | // Ensure C++11 mode is enabled. We rely on some C++11 construct, so we |
632 | // must enable C++11 by default. |
633 | // MSVC, GCC >= 5, Clang >= 6 defaults to C++14, but since we want to |
634 | // supports older compiler which defaults to C++98, we need to |
635 | // explicitly set the "-std" flag. |
636 | // Ideally should be done by https://github.com/alexcrichton/cc-rs/issues/191 |
637 | if !self.std_flag_set { |
638 | self.cc.flag_if_supported("-std=c++11" ); |
639 | } |
640 | // Build the C++ library |
641 | if let Err(e) = self.cc.file(filename).try_compile(LIB_NAME) { |
642 | let _ = writeln!(std::io::stderr(), " \n\nerror occurred: {}\n\n" , e); |
643 | #[cfg (not(feature = "docs-only" ))] |
644 | std::process::exit(1); |
645 | } |
646 | } |
647 | } |
648 | |
649 | /// Run the `cpp` build process on the crate with a root at the given path. |
650 | /// Intended to be used within `build.rs` files. |
651 | pub fn build<P: AsRef<Path>>(path: P) { |
652 | Config::new().build(crate_root:path) |
653 | } |
654 | |