forked from EmbarkStudios/physx-rs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbuild.rs
648 lines (537 loc) Β· 20.4 KB
/
build.rs
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
#![allow(dead_code)]
use std::{env, path::PathBuf};
struct Environment {
host: String,
emit_debug_info: bool,
target_compiler: Option<String>,
target_os: String,
target_env: Option<String>,
mode: String,
static_crt: bool,
}
struct Context {
root: PathBuf,
builder: cc::Build,
env: Environment,
includes: Vec<PathBuf>,
}
impl Context {
fn add_includes(&mut self, rel_root: &str, includes: &[&str]) -> &mut Self {
let root = self.root.join(rel_root);
self.includes
.extend(includes.iter().map(|inc| root.join(inc)));
self
}
fn add_sources(&mut self, rel_root: &str, files: &[&str]) -> &mut Self {
let root = self.root.join(rel_root);
self.builder.files(files.iter().map(|src| {
let mut p = root.join(src);
p.set_extension("cpp");
p
}));
// Always add the src directory as an include as well
self.includes.push(root);
self
}
fn add_component(&mut self, name: &str, rel: Option<&str>, sources: &[&str]) -> &mut Self {
let mut src_dir = format!("source/{name}");
if let Some(rel) = rel {
src_dir.push('/');
src_dir.push_str(rel);
}
self.add_sources(&src_dir, sources);
let mut comproot = self.root.join("include");
comproot.push(name);
if comproot.exists() {
self.includes.push(comproot);
}
let mut comproot = self.root.join("source");
comproot.push(name);
comproot.push("include");
if comproot.exists() {
self.includes.push(comproot);
}
self
}
}
macro_rules! component {
($name:ident) => {
fn $name(ctx: &mut Context) {
let sources = include!(concat!("sources/", stringify!($name)));
ctx.add_component(stringify!($name), Some("src"), &sources);
}
};
}
component! {common}
component! {fastxml}
component! {lowlevelaabb}
component! {lowleveldynamics}
component! {physxcharacterkinematic}
component! {pvd}
component! {scenequery}
component! {simulationcontroller}
component! {task}
// The foundation component is really the only one that references platform
// specific compilands, so just calculate them here
fn foundation(ctx: &mut Context) {
let sources = include!("sources/foundation");
ctx.add_component("foundation", None, &sources);
let target_family = env::var("CARGO_CFG_TARGET_FAMILY").expect("TARGET_FAMILY not specified");
let sources = match target_family.as_str() {
"unix" => &include!("sources/foundation_unix"),
"windows" => &include!("sources/foundation_windows"),
other => panic!("unknown TARGET_FAMILY '{}'", other),
};
ctx.add_sources(&format!("source/foundation/{target_family}"), sources);
}
fn lowlevel(ctx: &mut Context) {
// API
ctx.builder
.file(ctx.root.join("source/lowlevel/api/src/px_globals.cpp"));
// pipeline
{
let sources = include!("sources/lowlevel_pipeline");
ctx.add_sources("source/lowlevel/common/src/pipeline", &sources);
}
// software, otherwise known as non-gpu
{
let sources = include!("sources/lowlevel_software");
ctx.add_sources("source/lowlevel/software/src", &sources);
}
ctx.add_includes(
"source/lowlevel",
&[
"api/include",
"common/include/collision",
"common/include/pipeline",
"common/include/utils",
"software/include",
],
);
}
fn vehicle(ctx: &mut Context) {
let sources = include!("sources/vehicle");
ctx.add_component("physxvehicle", Some("src"), &sources);
let sources = include!("sources/vehicle_metadata");
ctx.add_sources("source/physxvehicle/src/physxmetadata/src", &sources);
ctx.add_includes("source/physxvehicle/src/physxmetadata", &["include"]);
}
fn extensions(ctx: &mut Context) {
let sources = include!("sources/extensions");
ctx.add_component("physxextensions", Some("src"), &sources);
// metadata
ctx.add_sources(
"source/physxmetadata/extensions/src",
&["PxExtensionAutoGeneratedMetaDataObjects"],
);
// serialization
let sources = include!("sources/extensions_serialization");
ctx.add_sources("source/physxextensions/src/serialization", &sources);
ctx.add_includes("source/physxextensions/src/serialization", &["File"]);
// xml
let sources = include!("sources/extensions_xml");
ctx.add_sources("source/physxextensions/src/serialization/Xml", &sources);
// binary
let sources = include!("sources/extensions_binary");
ctx.add_sources("source/physxextensions/src/serialization/Binary", &sources);
// tet
let sources = include!("sources/extensions_tet");
ctx.add_sources("source/physxextensions/src/tet", &sources);
ctx.add_includes("source/physxmetadata/extensions", &["include"]);
}
fn geomutils(ctx: &mut Context) {
// root
let sources = include!("sources/geomutils");
ctx.add_sources("source/geomutils/src", &sources);
ctx.add_includes("source/geomutils", &["include"]);
// ccd
let sources = include!("sources/geomutils_ccd");
ctx.add_sources("source/geomutils/src/ccd", &sources);
// common
let sources = include!("sources/geomutils_common");
ctx.add_sources("source/geomutils/src/common", &sources);
// contact
let sources = include!("sources/geomutils_contact");
ctx.add_sources("source/geomutils/src/contact", &sources);
// convex
let sources = include!("sources/geomutils_convex");
ctx.add_sources("source/geomutils/src/convex", &sources);
// cooking
let sources = include!("sources/geomutils_cooking");
ctx.add_sources("source/geomutils/src/cooking", &sources);
// distance
let sources = include!("sources/geomutils_distance");
ctx.add_sources("source/geomutils/src/distance", &sources);
// gjk
let sources = include!("sources/geomutils_gjk");
ctx.add_sources("source/geomutils/src/gjk", &sources);
// hf
let sources = include!("sources/geomutils_hf");
ctx.add_sources("source/geomutils/src/hf", &sources);
// intersection
let sources = include!("sources/geomutils_intersection");
ctx.add_sources("source/geomutils/src/intersection", &sources);
// mesh
let sources = include!("sources/geomutils_mesh");
ctx.add_sources("source/geomutils/src/mesh", &sources);
// pcm
let sources = include!("sources/geomutils_pcm");
ctx.add_sources("source/geomutils/src/pcm", &sources);
// sweep
let sources = include!("sources/geomutils_sweep");
ctx.add_sources("source/geomutils/src/sweep", &sources);
}
fn cooking(ctx: &mut Context) {
// root
let sources = include!("sources/cooking");
ctx.add_sources("source/physxcooking/src", &sources);
ctx.add_includes("source/include", &["cooking"]);
}
fn physx(ctx: &mut Context) {
// metadata
{
let sources = ["PxAutoGeneratedMetaDataObjects", "PxMetaDataObjects"];
ctx.add_sources("source/physxmetadata/core/src", &sources);
ctx.add_includes("source", &["physxmetadata/core/include"]);
}
// immediate mode
ctx.builder.file(
ctx.root
.join("source/immediatemode/src/NpImmediateMode.cpp"),
);
// there's always a "core"
let sources = include!("sources/core");
ctx.add_sources("source/physx/src", &sources);
}
fn add_common(ctx: &mut Context) {
let shared_root = ctx.root.parent().unwrap().join("pxshared");
let builder = &mut ctx.builder;
let ccenv = &ctx.env;
let root = &ctx.root;
builder.cpp(true);
// These includes are used by pretty much everything so just add them first
if ccenv.target_os == "android" {
builder.define("ANDROID", None);
let ndk_path = PathBuf::from(
env::var("ANDROID_NDK_ROOT")
.expect("environment variable \"ANDROID_NDK_ROOT\" has not been set"),
);
let host_str = ccenv.host.as_str();
let ndk_toolchain = match host_str {
"x86_64-pc-windows-msvc" => "windows-x86_64",
"x86_64-unknown-linux-gnu" => "linux-x86_64",
"x86_64-apple-darwin" | "aarch64-apple-darwin" => "darwin-x86_64",
_ => panic!(
"Host triple {} is unsupported for cross-compilation to Android",
host_str
),
};
let sysroot_path = ndk_path
.join("toolchains/llvm/prebuilt")
.join(ndk_toolchain)
.join("sysroot");
if !sysroot_path.exists() {
panic!(
"Can't find Android NDK sysroot path \"{}\"",
sysroot_path.to_str().unwrap()
);
}
builder.flag(&format!("--sysroot={}", &sysroot_path.to_str().unwrap()));
builder.cpp_link_stdlib("c++");
}
ctx.includes.push(shared_root.join("include"));
ctx.includes.extend(
[
"include",
"source/foundation/include",
"source/common/src",
"source/filebuf/include", // only used by pvd
]
.iter()
.map(|inc| root.join(inc)),
);
// If we're targetting msvc, just silence all the annoying warnings
if ccenv.target_env.as_deref() == Some("msvc") {
builder
.define("_CRT_SECURE_NO_WARNINGS", None)
.define("_WINSOCK_DEPRECATED_NO_WARNINGS", None)
.define("_ITERATOR_DEBUG_LEVEL", "0");
}
// Always build as a static library
builder.define("PX_PHYSX_STATIC_LIB", None);
// Always disable GPU features, at least for now
builder.define("DISABLE_CUDA_PHYSX", None);
if ccenv.emit_debug_info {
builder.define("PX_DEBUG", None).define("PX_CHECKED", None);
}
builder.define("PX_SUPPORT_PVD", "1");
if cfg!(feature = "profile") {
builder.define("PX_PROFILE", "1");
}
// If we're on linux, we already require clang++ for structgen, for reasons,
// so just force clang++ for the normal compile as well...except in the case
// where a user has expliclity set CXX....
// We _also_ set it explicitly for mac hosts, due to cc-rs's current
// compiler detection, as macos uses cc still, but it's actually a symlink
// to clang++, but that means that cc rs will by default think the compiler
// is gcc
if (ccenv.host.contains("-linux-") || ccenv.host == "x86_64-apple-darwin")
&& ccenv.target_compiler.is_none()
{
builder.compiler("clang++");
}
let flags = if builder.get_compiler().is_like_clang() || builder.get_compiler().is_like_gnu() {
vec![
"-std=c++14",
// Disable all warnings
"-w",
// Many reinterpret_cast tricks in physx break down if we do not disable strict-aliasing
"-fno-strict-aliasing",
]
} else if builder.get_compiler().is_like_msvc() {
// Disable defaults since we disagree with cc in some cases, this
// means we have to manually set eg profile and debug flags that
// would normally be set by default
builder.no_default_flags(true);
// We don't care about logos, but we absolutley care about not having
// long compile times
let mut flags = vec!["-nologo", "/MP"];
if ccenv.static_crt {
flags.push("/MT");
} else {
flags.push("/MD");
}
if ccenv.emit_debug_info {
flags.push("/Z7");
}
if ccenv.mode.as_str() == "profile" {
flags.push("/O2");
}
flags.push("/std:c++14");
flags
} else {
vec![]
};
for flag in flags {
builder.flag(flag);
}
// Physx requires either _DEBUG or NDEBUG be set, fine. Except, NEVER set
// _DEBUG on windows, or at least for clang-cl, because it will then think
// it should link the debug version of the CRT, which will _never_ work
// for rust because rust _always_ links the release version of the CRT
// (either static or dynamic depending on the crt-static target feature),
// there is some internal code in physx that uses _DEBUG but I don't know
// if we will ever actually care. That being said, this is all terrible.
builder.define("NDEBUG", "1");
// cc sets PIC by default for most targets, but if we're compiling with
// clang for windows, we need to unset it, as clang (at least as of 9)
// doesn't support it
if builder.get_compiler().is_like_clang() && ccenv.target_os == "windows" {
builder.pic(false);
}
}
fn cc_compile(target_env: Environment) {
let root = env::current_dir().unwrap().join("physx/physx");
let ccenv = target_env;
let mut ctx = Context {
builder: cc::Build::new(),
root,
env: ccenv,
includes: Vec::with_capacity(1000),
};
add_common(&mut ctx);
// Add the sources and includes for each major physx component
fastxml(&mut ctx);
task(&mut ctx);
foundation(&mut ctx);
lowlevel(&mut ctx);
lowlevelaabb(&mut ctx);
lowleveldynamics(&mut ctx);
vehicle(&mut ctx);
extensions(&mut ctx);
physxcharacterkinematic(&mut ctx);
common(&mut ctx);
geomutils(&mut ctx);
cooking(&mut ctx);
pvd(&mut ctx);
physx(&mut ctx);
scenequery(&mut ctx);
simulationcontroller(&mut ctx);
ctx.includes.push(ctx.root.join("source/pvd/include"));
// Strip out duplicate include paths, C++ already has it hard enough as it is
ctx.includes.sort();
ctx.includes.dedup();
for dir in ctx.includes {
ctx.builder.include(dir);
}
ctx.builder.compile("physx");
}
fn main() {
// Use the optimization level to determine the build profile to pass, we
// don't use cfg!(debug_assertions) here because I'm not sure what happens
// with that when build dependencies are configured to be debug and the
// actual target is meant to be release, so this seems safer
let build_mode = match env::var("OPT_LEVEL")
.ok()
.and_then(|s| s.parse::<i32>().ok())
.unwrap_or(1)
{
0 => "debug",
_ => "profile",
};
let target = env::var("TARGET").expect("TARGET not specified");
let host = env::var("HOST").expect("HOST not specified");
// Acquire the user-specified c++ compiler if one has been set, in the same
// order and manner that cc-rs will do it
let compiler = {
env::var(format!("CXX_{target}"))
.or_else(|_| {
let target_under = target.replace('-', "_");
env::var(format!("CXX_{target_under}"))
})
.or_else(|_| env::var("TARGET_CXX"))
.or_else(|_| env::var("CXX"))
.ok()
};
{
let target_os = env::var("CARGO_CFG_TARGET_OS").expect("target os not specified");
let target_env = env::var("CARGO_CFG_TARGET_ENV").ok();
let static_crt = env::var("CARGO_CFG_TARGET_FEATURE")
.unwrap_or_default()
.contains("crt-static");
let environment = Environment {
emit_debug_info: env::var("DEBUG")
.ok()
.and_then(|s| s.parse::<bool>().ok())
.unwrap_or(false),
target_compiler: compiler.clone(),
target_os,
target_env,
mode: build_mode.to_owned(),
host: host.clone(),
static_crt,
};
cc_compile(environment);
}
let mut cc_builder = cc::Build::new();
let physx_cc = cc_builder
.cpp(true)
.opt_level(3)
.debug(false)
.use_plt(false)
.warnings(false)
.extra_warnings(false)
.define("NDEBUG", None)
.define("PX_PHYSX_STATIC_LIB", None)
.include("physx/physx/include")
.include("physx/pxshared/include")
.include("physx/physx/source/foundation/include");
if cfg!(feature = "profile") {
physx_cc.define("PX_PROFILE", Some("1"));
}
if compiler.is_none() && host.contains("-linux-") {
physx_cc.compiler("clang++");
}
physx_cc.flag(if physx_cc.get_compiler().is_like_msvc() {
"/std:c++14"
} else {
"-std=c++14"
});
use std::ffi::OsString;
let output_dir_path =
PathBuf::from(env::var("OUT_DIR").expect("output directory not specified"));
let include_path = if env::var("CARGO_FEATURE_STRUCTGEN").is_ok() {
let mut structgen_path = output_dir_path.join("structgen");
// A bit hacky and might not work in all scenarios but qemu-aarch64 is not always
// available or even needed. If you are cross compiling to android then you need
// to remember to set CXX and CC to the respective toolchain compilers found in
// the ANDROID_NDK_ROOT as well.
let is_cross_compiling_aarch64 = target != host && target.starts_with("aarch64-");
let structgen_compiler = physx_cc.get_compiler();
let mut cmd = structgen_compiler.to_command();
if env::var("CARGO_FEATURE_CPP_WARNINGS").is_err() {
let dw = if physx_cc.get_compiler().is_like_clang()
|| physx_cc.get_compiler().is_like_gnu()
{
"-w"
} else if physx_cc.get_compiler().is_like_msvc() {
"/w"
} else {
panic!("unknown compiler");
};
cmd.arg(dw);
}
if structgen_compiler.is_like_msvc() {
let mut s = OsString::from("/Fe");
s.push(&structgen_path);
cmd.arg(s);
let mut s = OsString::from("/Fo");
s.push(&structgen_path);
s.push(".obj");
cmd.arg(s);
} else {
if is_cross_compiling_aarch64 {
// statically linking is just much easier to deal
// with when using qemu-aarch64
cmd.arg("-static");
}
cmd.arg("-o").arg(&structgen_path);
}
cmd.arg("src/structgen/structgen.cpp");
cmd.status().expect("c++ compiler failed to execute");
// The above status check has been shown to fail, ie, the compiler
// fails to output a binary, but reports success anyway
if host.contains("-windows-") {
structgen_path.set_extension("exe");
}
std::fs::metadata(&structgen_path)
.expect("failed to compile structgen even though compiler reported no failures");
let mut structgen = if is_cross_compiling_aarch64 {
let mut structgen = std::process::Command::new("qemu-aarch64");
structgen.arg(&structgen_path);
structgen
} else {
std::process::Command::new(&structgen_path)
};
structgen.current_dir(&output_dir_path);
structgen.status().expect("structgen failed to execute, if you are cross compiling to aarch64 you need to have qemu-aarch64 installed");
println!("cargo:rerun-if-changed=src/structgen/structgen.cpp");
println!("cargo:rerun-if-changed=src/structgen/structgen.hpp");
output_dir_path
} else {
let mut include = PathBuf::from("src/generated");
if target == "x86_64-pc-windows-msvc" {
include.push(target);
} else if target.contains("-linux-") || target.ends_with("apple-darwin") {
// Note that (currently) the x86_64 and aarch64 structures we bind
// are the exact same for linux/android and MacOS (unsure about iOS, but also don't care)
include.push("unix");
} else {
panic!("unknown TARGET triple '{}'", target);
}
include
};
// Disable all warnings. The rationale for this is that end users don't care
// and the physx code is incredibly sloppy with warnings since it's mostly
// developed on windows
if env::var("CARGO_FEATURE_CPP_WARNINGS").is_err() {
let dw = if physx_cc.get_compiler().is_like_clang() || physx_cc.get_compiler().is_like_gnu()
{
"-w"
} else if physx_cc.get_compiler().is_like_msvc() {
"/w"
} else {
panic!("unknown compiler");
};
physx_cc.flag(dw);
}
physx_cc
.include(include_path)
.file("src/physx_api.cpp")
.compile("physx_api");
println!("cargo:rerun-if-changed=src/physx_generated.hpp");
println!("cargo:rerun-if-changed=src/physx_generated.rs");
println!("cargo:rerun-if-changed=src/physx_api.cpp");
// TODO: use the cloned git revision number instead
println!("cargo:rerun-if-changed=physx/physx/include/foundation/PxPhysicsVersion.h");
}