1use std::{path::PathBuf, str::FromStr};
2use structopt::StructOpt;
3
4use cmake;
5use dirs;
6use git2::build::RepoBuilder;
7use thiserror::Error;
8use tracing::{self, info};
9
10use super::targets::Target;
11
12const TVM_REPO: &'static str = "https://github.com/apache/tvm";
13const DEFAULT_BRANCH: &'static str = "main";
14
15#[derive(Debug, Error)]
16pub enum Error {
17 #[error("{0}")]
18 Git2(#[from] git2::Error),
19 #[error("{0}")]
20 IoError(#[from] std::io::Error),
21 #[error("the directory does not exist: {0}")]
22 DirectoryNotFound(String),
23 #[error("the requested revision ({revision}) and repository ({repository}) combination does not exist.")]
24 RevisionNotFound {
25 revision: String,
26 repository: String,
27 },
28}
29
30#[derive(Debug)]
35pub enum CMakeSetting {
36 On,
37 Off,
38 Path(PathBuf),
39}
40
41impl FromStr for CMakeSetting {
42 type Err = std::convert::Infallible;
43
44 fn from_str(s: &str) -> Result<Self, Self::Err> {
45 match s.to_ascii_lowercase().as_str() {
46 "on" => Ok(CMakeSetting::On),
47 "off" => Ok(CMakeSetting::Off),
48 _ => Ok(CMakeSetting::Path(PathBuf::from_str(s)?)),
49 }
50 }
51}
52
53trait CMakeSettingValue {
55 fn as_string_value(&self) -> String;
56}
57
58impl CMakeSettingValue for &bool {
59 fn as_string_value(&self) -> String {
60 if **self { "ON" } else { "OFF" }.to_string()
61 }
62}
63
64impl CMakeSettingValue for &PathBuf {
65 fn as_string_value(&self) -> String {
66 format!("{}", self.display())
67 }
68}
69
70impl CMakeSettingValue for &String {
71 fn as_string_value(&self) -> String {
72 self.to_string()
73 }
74}
75
76impl CMakeSettingValue for &CMakeSetting {
77 fn as_string_value(&self) -> String {
78 match self {
79 CMakeSetting::On => "ON".to_string(),
80 CMakeSetting::Off => "OFF".to_string(),
81 CMakeSetting::Path(path) => path.as_string_value(),
82 }
83 }
84}
85
86#[derive(Debug, Default, StructOpt)]
87pub struct UserSettings {
88 #[structopt(long)]
91 pub use_cuda: Option<CMakeSetting>,
92 #[structopt(long)]
94 pub use_opencl: Option<CMakeSetting>,
95 #[structopt(long)]
97 pub use_vulkan: Option<CMakeSetting>,
98 #[structopt(long)]
99 pub use_metal: Option<CMakeSetting>,
100 #[structopt(long)]
101 pub use_rocm: Option<CMakeSetting>,
102 #[structopt(long)]
104 pub rocm_path: Option<PathBuf>,
105 #[structopt(long)]
107 pub use_hexagon_device: Option<bool>,
108 #[structopt(long)]
110 pub use_heaxgon_dsk: Option<PathBuf>,
111 #[structopt(long)]
113 pub use_rpc: Option<bool>,
114 #[structopt(long)]
116 pub use_threads: Option<bool>,
117 #[structopt(long)]
119 pub use_llvm: Option<CMakeSetting>,
120 #[structopt(long)]
122 pub use_stackvm_runtime: Option<bool>,
123 #[structopt(long)]
125 pub use_graph_runtime: Option<bool>,
126 #[structopt(long)]
128 pub use_graph_runtime_debug: Option<bool>,
129 #[structopt(long)]
131 pub use_openmp: Option<bool>,
132 #[structopt(long)]
134 pub use_relay_debug: Option<bool>,
135 #[structopt(long)]
137 pub use_rtti: Option<bool>,
138 #[structopt(long)]
140 pub use_mscv_mt: Option<bool>,
141 #[structopt(long)]
143 pub use_micro: Option<bool>,
144 #[structopt(long)]
146 pub use_install_dev: Option<bool>,
147 #[structopt(long)]
149 pub hide_private_symbols: Option<bool>,
150 #[structopt(long)]
152 pub use_fallback_stl_map: Option<bool>,
153 #[structopt(long)]
155 pub use_ethosn: Option<bool>,
156 #[structopt(long)]
158 pub use_index_default_i64: Option<bool>,
159 #[structopt(long)]
161 pub use_tf_tvmdsoop: Option<bool>,
162
163 #[structopt(long)]
166 pub use_byodt_posit: Option<bool>,
167 #[structopt(long)]
169 pub use_blas: Option<String>,
170 #[structopt(long)]
172 pub use_mkl: Option<CMakeSetting>,
173 #[structopt(long)]
175 pub use_mkldnn: Option<CMakeSetting>,
176 #[structopt(long)]
178 pub use_dnnl_codegen: Option<bool>,
179 #[structopt(long)]
181 pub use_cudnn: Option<bool>,
182 #[structopt(long)]
184 pub use_cublas: Option<bool>,
185 #[structopt(long)]
187 pub use_thrust: Option<bool>,
188 #[structopt(long)]
190 pub use_miopen: Option<bool>,
191 #[structopt(long)]
193 pub use_rocblas: Option<bool>,
194 #[structopt(long)]
196 pub use_sort: Option<bool>,
197 #[structopt(long)]
199 pub use_nnpack: Option<bool>,
200 #[structopt(long)]
202 pub use_random: Option<bool>,
203 #[structopt(long)]
205 pub use_micro_standalone_runtime: Option<bool>,
206 #[structopt(long)]
208 pub use_cpp_rpc: Option<bool>,
209 #[structopt(long)]
211 pub use_tflite: Option<bool>,
212 #[structopt(long)]
214 pub use_tensorflow_path: Option<PathBuf>,
215 #[structopt(long)]
217 pub use_coreml: Option<bool>,
218 #[structopt(long)]
220 pub use_target_onnx: Option<bool>,
221 #[structopt(long)]
223 pub use_arm_compute_lib: Option<bool>,
224 #[structopt(long)]
226 pub use_arm_compute_lib_graph_runtime: Option<CMakeSetting>,
227 #[structopt(long)]
229 pub use_tensorrt_codegen: Option<bool>,
230 #[structopt(long)]
232 pub use_tensorrt_runtime: Option<CMakeSetting>,
233 #[structopt(long)]
235 pub use_rust_ext: Option<String>,
236 #[structopt(long)]
238 pub use_vitis_ai: Option<bool>,
239 #[structopt(long)]
252 pub build_static_runtime: Option<bool>,
253}
254
255#[derive(Debug)]
256pub struct BuildConfig {
257 pub repository: Option<String>,
258 pub repository_path: Option<String>,
259 pub output_path: Option<String>,
260 pub branch: Option<String>,
261 pub verbose: bool,
262 pub clean: bool,
263 pub settings: UserSettings,
264}
265
266impl std::default::Default for BuildConfig {
267 fn default() -> BuildConfig {
268 BuildConfig {
269 repository: None,
270 repository_path: None,
271 output_path: None,
272 branch: None,
273 verbose: false,
274 clean: false,
275 settings: UserSettings::default(),
276 }
277 }
278}
279
280pub(crate) fn tvm_build_directory() -> PathBuf {
282 let home_dir = dirs::home_dir().expect("requires a home directory");
283 home_dir.join(format!(".tvm_build_{}", std::env::consts::ARCH))
284}
285
286impl BuildConfig {
287 pub fn get_revision(&self) -> Result<Revision, Error> {
289 info!("tvm_build::build");
290 let repository_url = self.repository.clone().unwrap_or(TVM_REPO.into());
291
292 let branch = self.branch.clone().unwrap_or(DEFAULT_BRANCH.into());
293 let revision = Revision::new(branch, self.output_path.clone());
294
295 let revision_path = match &self.repository_path {
296 Some(path) => std::path::Path::new(&path).into(),
297 None => revision.path(),
299 };
300
301 if revision_path.exists() && self.clean && self.repository_path.is_none() {
304 std::fs::remove_dir_all(&revision_path)?;
306 }
307
308 if !revision.source_path().exists() {
309 let mut repo_builder = RepoBuilder::new();
310 repo_builder.branch(&revision.revision);
311 println!("{}", repository_url);
312 let repo_path = revision_path.join("source");
313 let repo = match repo_builder.clone(&repository_url, &repo_path) {
314 Ok(repo) => Ok(repo),
315 Err(e) => Err(match e.code() {
316 git2::ErrorCode::NotFound => Error::RevisionNotFound {
317 repository: repository_url,
318 revision: revision.revision.clone(),
319 },
320 _ => e.into(),
321 }),
322 }?;
323 let submodules = repo.submodules()?;
325 for mut submodule in submodules {
326 submodule.update(true, None)?;
327 }
328 }
329
330 Ok(revision)
331 }
332
333 fn setting_key_value<T: CMakeSettingValue>(k: &str, value: T) -> (String, String) {
334 (k.to_string(), value.as_string_value())
335 }
336
337 pub fn as_cmake_define_key_values(&self) -> impl Iterator<Item = (String, String)> {
339 let UserSettings {
340 use_cuda,
341 use_opencl,
342 use_vulkan,
343 use_metal,
344 use_rocm,
345 rocm_path,
346 use_hexagon_device,
347 use_heaxgon_dsk,
348 use_rpc,
349 use_threads,
350 use_llvm,
351 use_stackvm_runtime,
352 use_graph_runtime,
353 use_graph_runtime_debug,
354 use_openmp,
355 use_relay_debug,
356 use_rtti,
357 use_mscv_mt,
358 use_micro,
359 use_install_dev,
360 hide_private_symbols,
361 use_fallback_stl_map,
362 use_ethosn,
363 use_index_default_i64,
364 use_tf_tvmdsoop,
365 use_byodt_posit,
366 use_blas,
367 use_mkl,
368 use_mkldnn,
369 use_dnnl_codegen,
370 use_cudnn,
371 use_cublas,
372 use_thrust,
373 use_miopen,
374 use_rocblas,
375 use_sort,
376 use_nnpack,
377 use_random,
378 use_micro_standalone_runtime,
379 use_cpp_rpc,
380 use_tflite,
381 use_tensorflow_path,
382 use_coreml,
383 use_target_onnx,
384 use_arm_compute_lib,
385 use_arm_compute_lib_graph_runtime,
386 use_tensorrt_codegen,
387 use_tensorrt_runtime,
388 use_rust_ext,
389 use_vitis_ai,
390 build_static_runtime
391 } = &self.settings;
392
393 vec![
394 use_cuda
395 .as_ref()
396 .map(|s| Self::setting_key_value("USE_CUDA", s)),
397 use_opencl
398 .as_ref()
399 .map(|s| Self::setting_key_value("USE_OPENCL", s)),
400 use_vulkan
401 .as_ref()
402 .map(|s| Self::setting_key_value("USE_VULKAN", s)),
403 use_metal
404 .as_ref()
405 .map(|s| Self::setting_key_value("USE_METAL", s)),
406 use_rocm
407 .as_ref()
408 .map(|s| Self::setting_key_value("USE_ROCM", s)),
409 rocm_path
410 .as_ref()
411 .map(|s| Self::setting_key_value("ROCM_PATH", s)),
412 use_hexagon_device
413 .as_ref()
414 .map(|s| Self::setting_key_value("USE_HEXAGON_DEVICE", s)),
415 use_heaxgon_dsk
416 .as_ref()
417 .map(|s| Self::setting_key_value("USE_HEAXGON_DSK", s)),
418 use_rpc
419 .as_ref()
420 .map(|s| Self::setting_key_value("USE_RPC", s)),
421 use_threads
422 .as_ref()
423 .map(|s| Self::setting_key_value("USE_THREADS", s)),
424 use_llvm
425 .as_ref()
426 .map(|s| Self::setting_key_value("USE_LLVM", s)),
427 use_stackvm_runtime
428 .as_ref()
429 .map(|s| Self::setting_key_value("USE_STACKVM_RUNTIME", s)),
430 use_graph_runtime
431 .as_ref()
432 .map(|s| Self::setting_key_value("USE_GRAPH_RUNTIME", s)),
433 use_graph_runtime_debug
434 .as_ref()
435 .map(|s| Self::setting_key_value("USE_GRAPH_RUNTIME_DEBUG", s)),
436 use_openmp
437 .as_ref()
438 .map(|s| Self::setting_key_value("USE_OPENMP", s)),
439 use_relay_debug
440 .as_ref()
441 .map(|s| Self::setting_key_value("USE_RELAY_DEBUG", s)),
442 use_rtti
443 .as_ref()
444 .map(|s| Self::setting_key_value("USE_RTTI", s)),
445 use_mscv_mt
446 .as_ref()
447 .map(|s| Self::setting_key_value("USE_MSCV_MT", s)),
448 use_micro
449 .as_ref()
450 .map(|s| Self::setting_key_value("USE_MICRO", s)),
451 use_install_dev
452 .as_ref()
453 .map(|s| Self::setting_key_value("USE_INSTALL_DEV", s)),
454 hide_private_symbols
455 .as_ref()
456 .map(|s| Self::setting_key_value("HIDE_PRIVATE_SYMBOLS", s)),
457 use_fallback_stl_map
458 .as_ref()
459 .map(|s| Self::setting_key_value("USE_FALLBACK_STL_MAP", s)),
460 use_ethosn
461 .as_ref()
462 .map(|s| Self::setting_key_value("USE_ETHOSN", s)),
463 use_index_default_i64
464 .as_ref()
465 .map(|s| Self::setting_key_value("USE_INDEX_DEFAULT_I64", s)),
466 use_tf_tvmdsoop
467 .as_ref()
468 .map(|s| Self::setting_key_value("USE_TF_TVMDSOOP", s)),
469 use_byodt_posit
470 .as_ref()
471 .map(|s| Self::setting_key_value("USE_BYODT_POSIT", s)),
472 use_blas
473 .as_ref()
474 .map(|s| Self::setting_key_value("USE_BLAS", s)),
475 use_mkl
476 .as_ref()
477 .map(|s| Self::setting_key_value("USE_MKL", s)),
478 use_mkldnn
479 .as_ref()
480 .map(|s| Self::setting_key_value("USE_MKLDNN", s)),
481 use_dnnl_codegen
482 .as_ref()
483 .map(|s| Self::setting_key_value("USE_DNNL_CODEGEN", s)),
484 use_cudnn
485 .as_ref()
486 .map(|s| Self::setting_key_value("USE_CUDNN", s)),
487 use_cublas
488 .as_ref()
489 .map(|s| Self::setting_key_value("USE_CUBLAS", s)),
490 use_thrust
491 .as_ref()
492 .map(|s| Self::setting_key_value("USE_THRUST", s)),
493 use_miopen
494 .as_ref()
495 .map(|s| Self::setting_key_value("USE_MIOPEN", s)),
496 use_rocblas
497 .as_ref()
498 .map(|s| Self::setting_key_value("USE_ROCBLAS", s)),
499 use_sort
500 .as_ref()
501 .map(|s| Self::setting_key_value("USE_SORT", s)),
502 use_nnpack
503 .as_ref()
504 .map(|s| Self::setting_key_value("USE_NNPACK", s)),
505 use_random
506 .as_ref()
507 .map(|s| Self::setting_key_value("USE_RANDOM", s)),
508 use_micro_standalone_runtime
509 .as_ref()
510 .map(|s| Self::setting_key_value("USE_MICRO_STANDALONE_RUNTIME", s)),
511 use_cpp_rpc
512 .as_ref()
513 .map(|s| Self::setting_key_value("USE_CPP_RPC", s)),
514 use_tflite
515 .as_ref()
516 .map(|s| Self::setting_key_value("USE_TFLITE", s)),
517 use_tensorflow_path
518 .as_ref()
519 .map(|s| Self::setting_key_value("USE_TENSORFLOW_PATH", s)),
520 use_coreml
521 .as_ref()
522 .map(|s| Self::setting_key_value("USE_COREML", s)),
523 use_target_onnx
524 .as_ref()
525 .map(|s| Self::setting_key_value("USE_TARGET_ONNX", s)),
526 use_arm_compute_lib
527 .as_ref()
528 .map(|s| Self::setting_key_value("USE_ARM_COMPUTE_LIB", s)),
529 use_arm_compute_lib_graph_runtime
530 .as_ref()
531 .map(|s| Self::setting_key_value("USE_ARM_COMPUTE_LIB_GRAPH_RUNTIME", s)),
532 use_tensorrt_codegen
533 .as_ref()
534 .map(|s| Self::setting_key_value("USE_TENSORRT_CODEGEN", s)),
535 use_tensorrt_runtime
536 .as_ref()
537 .map(|s| Self::setting_key_value("USE_TENSORRT_RUNTIME", s)),
538 use_rust_ext
539 .as_ref()
540 .map(|s| Self::setting_key_value("USE_RUST_EXT", s)),
541 use_vitis_ai
542 .as_ref()
543 .map(|s| Self::setting_key_value("USE_VITIS_AI", s)),
544 build_static_runtime
545 .as_ref()
546 .map(|s| Self::setting_key_value("BUILD_STATIC_RUNTIME", s)),
547 ]
548 .into_iter()
549 .flatten()
550 }
551}
552
553pub struct Revision {
554 revision: String,
555 output_path: Option<String>,
556}
557
558impl Revision {
559 pub fn new(revision: String, output_path: Option<String>) -> Revision {
560 Revision { revision, output_path }
561 }
562
563 pub fn path(&self) -> PathBuf {
564 let path =
565 if let Some(path) = self.output_path.as_ref() {
566 PathBuf::from(path)
567 } else {
568 tvm_build_directory()
569 };
570 path.join(&self.revision)
571 }
572
573 pub fn source_path(&self) -> PathBuf {
574 self.path().join("source")
575 }
576
577 pub fn build_path(&self) -> PathBuf {
578 self.path().join("build")
579 }
580
581 pub fn build_for(&self, build_config: &BuildConfig, target: Target) -> Result<(), Error> {
582 let source_path = self.source_path();
583 let build_path = self.build_path();
584
585 if !build_path.exists() {
586 std::fs::create_dir_all(build_path.clone())?;
587 }
591
592 let mut cmake_config = cmake::Config::new(source_path.clone());
593
594 cmake_config
595 .generator("Unix Makefiles")
596 .out_dir(build_path.clone())
597 .target(&target.target_str)
598 .host(&target.host)
599 .profile("Debug");
600
601 for (key, value) in build_config.as_cmake_define_key_values() {
602 let _ = cmake_config.define(key, value);
603 }
604
605 if build_config.verbose {
606 cmake_config.very_verbose(true);
607 }
608
609 cmake_config.build();
610
611 Ok(())
612 }
613}
614
615pub struct BuildResult {
616 pub revision: Revision,
617}