tvm_build/
core.rs

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/// Many TVM CMake settings are either OFF (disabled), ON (with auto detection) or
31/// a path implying on with a fixed configuration.
32///
33/// This enumeration represents all cases in a more Rust friendly way.
34#[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
53/// Convert something into a value that can be used in `cmake::Config::define`.
54trait 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    // TVM Build Configuration Options
89    /// Build with the CUDA support enabled.
90    #[structopt(long)]
91    pub use_cuda: Option<CMakeSetting>,
92    /// Build with the CUDA runtime enabled.
93    #[structopt(long)]
94    pub use_opencl: Option<CMakeSetting>,
95    // Build with Vulkan runtime enabled.
96    #[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    /// The path to ROCM.
103    #[structopt(long)]
104    pub rocm_path: Option<PathBuf>,
105    /// Build with Hexagon device support in TVM runtime.
106    #[structopt(long)]
107    pub use_hexagon_device: Option<bool>,
108    /// Path to the Hexagon SDK root (required for Hexagon support in TVM runtime or for building TVM runtime for Hexagon.
109    #[structopt(long)]
110    pub use_heaxgon_dsk: Option<PathBuf>,
111    /// Whether to enable TVM RPC.
112    #[structopt(long)]
113    pub use_rpc: Option<bool>,
114    /// Build with threading support.
115    #[structopt(long)]
116    pub use_threads: Option<bool>,
117    /// Build with LLVM, can also be set to specific llvm-config path.
118    #[structopt(long)]
119    pub use_llvm: Option<CMakeSetting>,
120    /// Enable TVM's stackvm in the runtime.
121    #[structopt(long)]
122    pub use_stackvm_runtime: Option<bool>,
123    /// Build with graph runtime, defaults to ON.
124    #[structopt(long)]
125    pub use_graph_runtime: Option<bool>,
126    /// Build with graph runtime debug mode, defaults to OFF.
127    #[structopt(long)]
128    pub use_graph_runtime_debug: Option<bool>,
129    /// Build with OpenMP thread pool implementation, defaults to OFF.
130    #[structopt(long)]
131    pub use_openmp: Option<bool>,
132    /// Build Relay in debug mode, defaults to OFF.
133    #[structopt(long)]
134    pub use_relay_debug: Option<bool>,
135    /// Build with RTTI, defaults to ON.
136    #[structopt(long)]
137    pub use_rtti: Option<bool>,
138    /// Build with multi-threaded MSCV runtime.
139    #[structopt(long)]
140    pub use_mscv_mt: Option<bool>,
141    /// Build with Micro TVM support.
142    #[structopt(long)]
143    pub use_micro: Option<bool>,
144    /// Install compiler infrastructure, defaults to OFF.
145    #[structopt(long)]
146    pub use_install_dev: Option<bool>,
147    /// Compile with -fvisibility=hidden.
148    #[structopt(long)]
149    pub hide_private_symbols: Option<bool>,
150    /// Use TVM's POD compatible Map, defaults to OFF.
151    #[structopt(long)]
152    pub use_fallback_stl_map: Option<bool>,
153    /// tvm_option(USE_ETHOSN "Build with Arm Ethos-N" OFF)
154    #[structopt(long)]
155    pub use_ethosn: Option<bool>,
156    /// Defaults the index datatype to int64.
157    #[structopt(long)]
158    pub use_index_default_i64: Option<bool>,
159    /// Build with TensorFlow TVMDSOOp.
160    #[structopt(long)]
161    pub use_tf_tvmdsoop: Option<bool>,
162
163    // Contrib library options.
164    /// Build with BYODT software emulated posit custom datatype.
165    #[structopt(long)]
166    pub use_byodt_posit: Option<bool>,
167    /// The blas library to be linked.
168    #[structopt(long)]
169    pub use_blas: Option<String>,
170    // tvm_option(USE_MKL "MKL root path when use MKL blas" OFF)
171    #[structopt(long)]
172    pub use_mkl: Option<CMakeSetting>,
173    /// "Build with MKLDNN"
174    #[structopt(long)]
175    pub use_mkldnn: Option<CMakeSetting>,
176    /// Enable MKLDNN (DNNL) codegen.
177    #[structopt(long)]
178    pub use_dnnl_codegen: Option<bool>,
179    // tvm_opt"Build with cuDNN" OFF)
180    #[structopt(long)]
181    pub use_cudnn: Option<bool>,
182    // tvm_option(USE_CUBLAS "Build with cuBLAS" OFF)
183    #[structopt(long)]
184    pub use_cublas: Option<bool>,
185    // tvm_option(USE_THRUST "Build with Thrust" OFF)
186    #[structopt(long)]
187    pub use_thrust: Option<bool>,
188    // tvm_option(USE_MIOPEN "Build with ROCM:MIOpen" OFF)
189    #[structopt(long)]
190    pub use_miopen: Option<bool>,
191    // tvm_option(USE_ROCBLAS "Build with ROCM:RoCBLAS" OFF)
192    #[structopt(long)]
193    pub use_rocblas: Option<bool>,
194    // tvm_option(USE_SORT "Build with sort support" ON)
195    #[structopt(long)]
196    pub use_sort: Option<bool>,
197    // tvm_option(USE_NNPACK "Build with nnpack support" OFF)
198    #[structopt(long)]
199    pub use_nnpack: Option<bool>,
200    // tvm_option(USE_RANDOM "Build with random support" ON)
201    #[structopt(long)]
202    pub use_random: Option<bool>,
203    // tvm_option(USE_MICRO_STANDALONE_RUNTIME "Build with micro.standalone_runtime support" OFF)
204    #[structopt(long)]
205    pub use_micro_standalone_runtime: Option<bool>,
206    // tvm_option(USE_CPP_RPC "Build CPP RPC" OFF)
207    #[structopt(long)]
208    pub use_cpp_rpc: Option<bool>,
209    // tvm_option(USE_TFLITE "Build with tflite support" OFF)
210    #[structopt(long)]
211    pub use_tflite: Option<bool>,
212    // tvm_option(USE_TENSORFLOW_PATH "TensorFlow root path when use TFLite" none)
213    #[structopt(long)]
214    pub use_tensorflow_path: Option<PathBuf>,
215    // tvm_option(USE_COREML "Build with coreml support" OFF)
216    #[structopt(long)]
217    pub use_coreml: Option<bool>,
218    // tvm_option(USE_TARGET_ONNX "Build with ONNX Codegen support" OFF)
219    #[structopt(long)]
220    pub use_target_onnx: Option<bool>,
221    // tvm_option(USE_ARM_COMPUTE_LIB "Build with Arm Compute Library" OFF)
222    #[structopt(long)]
223    pub use_arm_compute_lib: Option<bool>,
224    // tvm_option(USE_ARM_COMPUTE_LIB_GRAPH_RUNTIME "Build with Arm Compute Library graph runtime" OFF)
225    #[structopt(long)]
226    pub use_arm_compute_lib_graph_runtime: Option<CMakeSetting>,
227    /// Build with TensorRT Codegen support, defaults to OFF>
228    #[structopt(long)]
229    pub use_tensorrt_codegen: Option<bool>,
230    /// Build with TensorRT runtime, defaults to OFF.
231    #[structopt(long)]
232    pub use_tensorrt_runtime: Option<CMakeSetting>,
233    /// Build with Rust based compiler extensions, defaults to OFF.
234    #[structopt(long)]
235    pub use_rust_ext: Option<String>,
236    /// Build with VITIS-AI Codegen support, defaults to OFF.
237    #[structopt(long)]
238    pub use_vitis_ai: Option<bool>,
239    // Note(@jroesch): these options are supported by TVM but not exposed by this interface
240    // we instead use defaults.
241    //
242    // Configuration for 3rdparty libraries.
243    // tvm_option(DLPACK_PATH "Path to DLPACK" "3rdparty/dlpack/include")
244    // tvm_option(DMLC_PATH "Path to DMLC" "3rdparty/dmlc-core/include")
245    // tvm_option(RANG_PATH "Path to RANG" "3rdparty/rang/include")
246    // tvm_option(COMPILER_RT_PATH "Path to COMPILER-RT" "3rdparty/compiler-rt")
247    // tvm_option(PICOJSON_PATH "Path to PicoJSON" "3rdparty/picojson")
248
249    /// Whether to build static libtvm_runtime.a, the default is to build the dynamic
250    /// version: libtvm_runtime.so.
251    #[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
280// convert to lazy<T>?
281pub(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    // TODO: split per revision
288    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            // todo: check that the provided path exists
298            None => revision.path(),
299        };
300
301        // If a user specifies the repository directory we assume we
302        // don't own it and won't clean it.
303        if revision_path.exists() && self.clean && self.repository_path.is_none() {
304            // This fails if doesn't exist
305            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            // todo(@jroesch): key build repos by sha? right now branch alone potentially conflicts.
324            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    // Returns any user settings to be "set" as cmake definitions.
338    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            // .map_err
588            // Err(err) =>
589            // .context(format!("the build directory does not exist: {:?}", build_path))?;
590        }
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}