File: mlpack.cmake

package info (click to toggle)
mlpack 4.6.2-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 31,272 kB
  • sloc: cpp: 226,039; python: 1,934; sh: 1,198; lisp: 414; makefile: 85
file content (764 lines) | stat: -rw-r--r-- 28,362 bytes parent folder | download
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
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
# - Find mlpack
# Find the mlpack C++ library
#
# author Omar Shrit

##===================================================
##  FUNCTION DOCUMENTATION
##===================================================
#
# find_mlpack() 
#----------------------
#
# Call this macro to find mlpack and its dependencies (Armadillo, ensmallen,
# cereal, and any Armadillo dependencies).
#
# This function will not automatically download any missing dependencies, and
# will instead throw errors if dependencies are not found.  For a version that
# automatically downloads dependencies, see `fetch_mlpack()`.
#
# Configuration options:
#
#   MLPACK_DISABLE_OPENMP: if set, parallelism via OpenMP will be disabled.
#   MLPACK_USE_SYSTEM_STB: if set, STB will be searched for on the system, 
#       instead of using the version bundled with mlpack.
#
# If mlpack is successfully found, the `MLPACK_FOUND` variable will be set to
# `TRUE`; otherwise, it will be set to `FALSE`.
#
# This macro will set the following variables:
# 
# MLPACK_INCLUDE_DIRS: list of all include directories for mlpack and its
#                      dependencies (Armadillo, cereal, ensmallen)
# MLPACK_LIBRARIES: list of all dependency libraries to link against (typically
#                   just OpenBLAS)
#
#
# fetch_mlpack(COMPILE_OPENBLAS)
#-----------------------
#
# This macro downloads the mlpack library and its dependencies.  Call this 
# function to find mlpack and its dependencies (Armadillo, ensmallen, cereal) on
# a system where mlpack or those dependencies may not be available.
#
# fetch_mlpack() accepts one parameter, `COMPILE_OPENBLAS`.  When this is set to
# `TRUE`, then OpenBLAS will be downloaded and compiled as a dependency of 
# Armadillo.  If `COMPILE_OPENBLAS` is set to `FALSE`, then it is expected that
# OpenBLAS or a BLAS/LAPACK library is already available on the system.  When 
# CMAKE_CROSSCOMPILING is set, then OpenBLAS is always compiled for the target 
# architecture.
#
# Other dependencies of mlpack do not need compilation, as they are all
# header-only.
#
# If mlpack is not found on the system, `fetch_mlpack()` will download the
# latest stable version of mlpack.
#
# Configuration options:
#
#   MLPACK_DISABLE_OPENMP: if set, parallelism via OpenMP will be disabled.
#   MLPACK_USE_SYSTEM_STB: if set, STB will be searched for on the system, 
#       instead of using the version bundled with mlpack.
#
# After all libraries are downloaded and set up, the macro will set the 
# following variables:
#
# MLPACK_INCLUDE_DIRS: list of all include directories for mlpack and its
#                      dependencies (Armadillo, cereal, ensmallen)
# MLPACK_LIBRARIES: list of all dependency libraries to link against (typically
#                   just OpenBLAS)
#
##===================================================
##  INTERNAL FUNCTION DOCUMENTATION
##===================================================
#
# get_deps(LINK DEPS_NAME PACKAGE)
#-------------------
#
# This macro allows to download dependenices from the link that is provided to
# them. You need to pass the LINK to download from, the name of
# the dependency, and the filename to store the downloaded package to such
# as armadillo.tar.gz and they are downloaded into 
# ${CMAKE_BINARY_DIR}/deps/${PACKAGE}
# At each download, this module sets a GENERIC_INCLUDE_DIR path,
# which means that you need to set the main path for the include
# directories for each package.
# Note that, the package should be compressed only as .tar.gz
#
#
# find_armadillo()
#------------------
#
# This macro finds armadillo library, and sets the necessary paths to each
# one of the parameters. If the library is not found this macro will set 
# ARMADILLO_FOUND to false.
#
# This macro sets the following variables:
#  ARMADILLO_FOUND - set to true if the library is found
#  ARMADILLO_INCLUDE_DIRS - list of required include directories
#  ARMADILLO_LIBRARIES - list of libraries to be linked
#  ARMADILLO_VERSION_MAJOR - major version number
#  ARMADILLO_VERSION_MINOR - minor version number
#  ARMADILLO_VERSION_PATCH - patch version number
#  ARMADILLO_VERSION_STRING - version number as a string (ex: "1.0.4")
#  ARMADILLO_VERSION_NAME - name of the version (ex: "Antipodean Antileech")
#
#
# find_ensmallen
#------------------
#
# This macro finds ensmallen library and sets the necessary paths to each
# one of the parameters. If the library is not found this function will set 
# ENSMALLEN_FOUND to false.
#
# This module sets the following variables:
#  ENSMALLEN_FOUND - set to true if the library is found
#  ENSMALLEN_INCLUDE_DIR - list of required include directories
#  ENSMALLEN_VERSION_MAJOR - major version number
#  ENSMALLEN_VERSION_MINOR - minor version number
#  ENSMALLEN_VERSION_PATCH - patch version number
#  ENSMALLEN_VERSION_STRING - version number as a string (ex: "1.0.4")
#  ENSMALLEN_VERSION_NAME - name of the version (ex: "Antipodean Antileech")
#
#
# find_cereal()
#------------------
#
# This macro finds cereal library and sets the necessary paths to each
# one of the parameters. If the library is not found this macro will set 
# CEREAL_FOUND to false.

# This module sets the following variables:
#  CEREAL_FOUND - set to true if the library is found
#  CEREAL_INCLUDE_DIR - list of required include directories
#  CEREAL_VERSION_MAJOR - major version number
#  CEREAL_VERSION_MINOR - minor version number
#  CEREAL_VERSION_PATCH - patch version number
#  CEREAL_VERSION_STRING - version number as a string (ex: "1.0.4")
#
#
# find stb()
#------------------
#
# This macro finds STB library and sets the necessary paths to each
# one of the parameters. If the library is not found this macro will set 
# STB_FOUND to false.
# 
# - Find STB_IMAGE
#
# This module sets the following variables:
#  STB_IMAGE_FOUND - set to true if the library is found
#  STB_IMAGE_INCLUDE_DIR - list of required include directories
#  STB_INCLUDE_NEEDS_STB_SUFFIX - whether or not the include files are under an
#     stb/ directory; if "YES", then includes must be done as, e.g.,
#     stb/stb_image.h.
#
#
# find_openmp()
#-----------------------
#
# This macro finds if OpenMP library is installed and supported by the
# compiler. if the library found then it sets the following parameters:
#  
# OpenMP_FOUND - set to true if the library is found
# 
#
# find_mlpack_internal()
#-----------------------
#
# This macro finds the mlpack library and sets the necessary paths to each one
# of the parameters. If the library is not found this macro will set
# MLPACK_FOUND to false.
#
# This module sets the following variables:
#  MLPACK_FOUND - set to true if the library is found
#  MLPACK_VERSION_MAJOR - major version number
#  MLPACK_VERSION_MINOR - minor version number
#  MLPACK_VERSION_PATCH - patch version number
#  MLPACK_VERSION_STRING - version number as a string (ex: "1.0.4")
#  MLPACK_INCLUDE_DIR - list of mlpack include directories
#
#
##===================================================
##  MLPACK DEPENDENCIES SETTINGS. 
##===================================================

# Set minimum library versions required by mlpack.
#
# For Armadillo, try to keep the minimum required version less than or equal to
# what's available on the current Ubuntu LTS or most recent stable RHEL release.
# See https://github.com/mlpack/mlpack/issues/3033 for some more discussion.
set(ARMADILLO_VERSION "10.8.2")
set(ENSMALLEN_VERSION "2.10.0")
set(CEREAL_VERSION "1.1.2")
set(OPENBLAS_VERSION "0.3.29")

# Set library version to be used when fetching them from the source. 
set(ARMADILLO_FETCH_VERSION "12.6.5")
set(ENSMALLEN_FETCH_VERSION "latest")
set(CEREAL_FETCH_VERSION "1.3.2")
set(MLPACK_FETCH_VERSION "latest")

# Set required standard to C++17.
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(MLPACK_DISABLE_OPENMP OFF)

##===================================================
##  MLPACK AUTODOWNLOADER DEPENDENCIES FUNCTIONS
##===================================================

# This function auto-downloads mlpack dependencies.
macro(get_deps LINK DEPS_NAME PACKAGE)
  if (NOT EXISTS "${CMAKE_BINARY_DIR}/deps/${PACKAGE}")
    file(DOWNLOAD ${LINK}
           "${CMAKE_BINARY_DIR}/deps/${PACKAGE}"
            STATUS DOWNLOAD_STATUS_LIST LOG DOWNLOAD_LOG
            SHOW_PROGRESS)
    list(GET DOWNLOAD_STATUS_LIST 0 DOWNLOAD_STATUS)
    if (DOWNLOAD_STATUS EQUAL 0)
      execute_process(COMMAND ${CMAKE_COMMAND} -E
          tar xf "${CMAKE_BINARY_DIR}/deps/${PACKAGE}"
          WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/deps/")
    else ()
      list(GET DOWNLOAD_STATUS_LIST 1 DOWNLOAD_ERROR)
      message(FATAL_ERROR
          "Could not download ${DEPS_NAME}! Error code ${DOWNLOAD_STATUS}: ${DOWNLOAD_ERROR}!  Error log: ${DOWNLOAD_LOG}")
    endif()
  endif()
  # Get the name of the directory.
  file (GLOB DIRECTORIES RELATIVE "${CMAKE_BINARY_DIR}/deps/"
      "${CMAKE_BINARY_DIR}/deps/${DEPS_NAME}*.*")
  if(${DEPS_NAME} MATCHES "stb")
    file (GLOB DIRECTORIES RELATIVE "${CMAKE_BINARY_DIR}/deps/"
        "${CMAKE_BINARY_DIR}/deps/${DEPS_NAME}")
  endif()
  # list(FILTER) is not available on 3.5 or older, but try to keep
  # configuring without filtering the list anyway 
  # (it works only if the file is present as .tar.gz).
  list(FILTER DIRECTORIES EXCLUDE REGEX ".*\.tar\.gz")
  list(LENGTH DIRECTORIES DIRECTORIES_LEN)
  if (DIRECTORIES_LEN GREATER 0)
    list(GET DIRECTORIES 0 DEPENDENCY_DIR)
    set(GENERIC_INCLUDE_DIR "${CMAKE_BINARY_DIR}/deps/${DEPENDENCY_DIR}/include")
    install(DIRECTORY "${GENERIC_INCLUDE_DIR}/" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
  else ()
    message(FATAL_ERROR
            "Problem unpacking ${DEPS_NAME}! Expected only one directory "
            "${DEPS_NAME};. Try to remove the directory ${CMAKE_BINARY_DIR}/deps and reconfigure.")
  endif ()
endmacro()

macro(find_armadillo)
  cmake_policy(PUSH)
  if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.29")
    cmake_policy(SET CMP0159 NEW) # file(STRINGS) with REGEX updates CMAKE_MATCH_<n>
  endif()

  set(CURRENT_PATH ${ARGN})
  if (CURRENT_PATH)
    find_path(ARMADILLO_INCLUDE_DIR
      NAMES armadillo
      PATHS "${CURRENT_PATH}/deps/Armadillo/include"
      NO_DEFAULT_PATH)
  else()
    find_path(ARMADILLO_INCLUDE_DIR
      NAMES armadillo
      PATHS "$ENV{ProgramFiles}/Armadillo/include")
  endif()
  if (ARMADILLO_INCLUDE_DIR)
    # ------------------------------------------------------------------------
    #  Extract version information from <armadillo>
    # ------------------------------------------------------------------------

    # WARNING: Early releases of Armadillo didn't have the arma_version.hpp file.
    # (e.g. v.0.9.8-1 in ubuntu maverick packages (2001-03-15))
    # If the file is missing, set all values to 0
    set(ARMADILLO_VERSION_MAJOR 0)
    set(ARMADILLO_VERSION_MINOR 0)
    set(ARMADILLO_VERSION_PATCH 0)
    set(ARMADILLO_VERSION_NAME "EARLY RELEASE")

    if (EXISTS "${ARMADILLO_INCLUDE_DIR}/armadillo_bits/arma_version.hpp")

      # Read and parse armdillo version header file for version number
      file(STRINGS "${ARMADILLO_INCLUDE_DIR}/armadillo_bits/arma_version.hpp" _ARMA_HEADER_CONTENTS REGEX "#define ARMA_VERSION_[A-Z]+ ")
      string(REGEX REPLACE ".*#define ARMA_VERSION_MAJOR ([0-9]+).*" "\\1" ARMADILLO_VERSION_MAJOR "${_ARMA_HEADER_CONTENTS}")
      string(REGEX REPLACE ".*#define ARMA_VERSION_MINOR ([0-9]+).*" "\\1" ARMADILLO_VERSION_MINOR "${_ARMA_HEADER_CONTENTS}")
      string(REGEX REPLACE ".*#define ARMA_VERSION_PATCH ([0-9]+).*" "\\1" ARMADILLO_VERSION_PATCH "${_ARMA_HEADER_CONTENTS}")

      # WARNING: The number of spaces before the version name is not one.
      string(REGEX REPLACE ".*#define ARMA_VERSION_NAME\ +\"([0-9a-zA-Z\ _-]+)\".*" "\\1" ARMADILLO_VERSION_NAME "${_ARMA_HEADER_CONTENTS}")
      set(ARMADILLO_FOUND YES)
    endif()

    set(ARMADILLO_VERSION_STRING "${ARMADILLO_VERSION_MAJOR}.${ARMADILLO_VERSION_MINOR}.${ARMADILLO_VERSION_PATCH}")
  endif()

  if (EXISTS "${ARMADILLO_INCLUDE_DIR}/armadillo_bits/config.hpp")
    file(STRINGS "${ARMADILLO_INCLUDE_DIR}/armadillo_bits/config.hpp" _ARMA_CONFIG_CONTENTS REGEX "^#define ARMA_USE_[A-Z]+")
    string(REGEX MATCH "ARMA_USE_WRAPPER" _ARMA_USE_WRAPPER "${_ARMA_CONFIG_CONTENTS}")
    string(REGEX MATCH "ARMA_USE_LAPACK" _ARMA_USE_LAPACK "${_ARMA_CONFIG_CONTENTS}")
    string(REGEX MATCH "ARMA_USE_BLAS" _ARMA_USE_BLAS "${_ARMA_CONFIG_CONTENTS}")
    string(REGEX MATCH "ARMA_USE_ARPACK" _ARMA_USE_ARPACK "${_ARMA_CONFIG_CONTENTS}")
    string(REGEX MATCH "ARMA_USE_HDF5" _ARMA_USE_HDF5 "${_ARMA_CONFIG_CONTENTS}")
  endif()

  # If _ARMA_USE_WRAPPER is set, then we just link to armadillo, but if it's not then we need support libraries instead
  set(_ARMA_SUPPORT_LIBRARIES)

  if(_ARMA_USE_WRAPPER)
    # Link to the armadillo wrapper library.
    find_library(ARMADILLO_LIBRARY
      NAMES armadillo
      NAMES_PER_DIR
      PATHS
        "$ENV{ProgramFiles}/Armadillo/lib"
        "$ENV{ProgramFiles}/Armadillo/lib64"
        "$ENV{ProgramFiles}/Armadillo"
      )
    set(_ARMA_REQUIRED_VARS ARMADILLO_LIBRARY)
  else()
    set(ARMADILLO_LIBRARY "")
  endif()

  # Transitive linking with the wrapper does not work with MSVC,
  # so we must *also* link against Armadillo's dependencies.
  if(NOT _ARMA_USE_WRAPPER OR MSVC)
    # Link directly to individual components.
    foreach(pkg
        LAPACK
        BLAS
        ARPACK
        HDF5
        )
      if(_ARMA_USE_${pkg})
        find_package(${pkg} QUIET)
        list(APPEND _ARMA_REQUIRED_VARS "${pkg}_FOUND")
        if(${pkg}_FOUND)
          list(APPEND _ARMA_SUPPORT_LIBRARIES ${${pkg}_LIBRARIES})
        endif()
      endif()
    endforeach()
  endif()
  if (ARMADILLO_FOUND)
    set(ARMADILLO_INCLUDE_DIRS ${ARMADILLO_INCLUDE_DIR})
    set(ARMADILLO_LIBRARIES ${ARMADILLO_LIBRARY} ${_ARMA_SUPPORT_LIBRARIES})
  endif()
  # Clean up internal variables
  unset(_ARMA_REQUIRED_VARS)
  unset(_ARMA_SUPPORT_LIBRARIES)
  unset(_ARMA_USE_WRAPPER)
  unset(_ARMA_USE_LAPACK)
  unset(_ARMA_USE_BLAS)
  unset(_ARMA_USE_ARPACK)
  unset(_ARMA_USE_HDF5)
  unset(_ARMA_CONFIG_CONTENTS)
  unset(_ARMA_HEADER_CONTENTS)

  cmake_policy(POP)
endmacro()

# Findcereal.cmake
macro(find_cereal)

  set(CURRENT_PATH ${ARGN})
  if (CURRENT_PATH)
    find_path(CEREAL_INCLUDE_DIR
      NAMES cereal
      PATHS "${CURRENT_PATH}/deps/cereal/include"
      NO_DEFAULT_PATH)
  else()
    find_path(CEREAL_INCLUDE_DIR
      NAMES cereal
      PATHS "$ENV{ProgramFiles}/cereal/include")
  endif()

  if (CEREAL_INCLUDE_DIR)
    # ------------------------------------------------------------------------
    #  Extract version information from <CEREAL>
    # ------------------------------------------------------------------------
    set(CEREAL_FOUND YES)
    set(CEREAL_VERSION_MAJOR 0)
    set(CEREAL_VERSION_MINOR 0)
    set(CEREAL_VERSION_PATCH 0)

    if (EXISTS "${CEREAL_INCLUDE_DIR}/cereal/version.hpp")

      # Read and parse cereal version header file for version number
      file(READ "${CEREAL_INCLUDE_DIR}/cereal/version.hpp"
          _CEREAL_HEADER_CONTENTS)
      string(REGEX REPLACE ".*#define CEREAL_VERSION_MAJOR ([0-9]+).*" "\\1"
          CEREAL_VERSION_MAJOR "${_CEREAL_HEADER_CONTENTS}")
      string(REGEX REPLACE ".*#define CEREAL_VERSION_MINOR ([0-9]+).*" "\\1"
          CEREAL_VERSION_MINOR "${_CEREAL_HEADER_CONTENTS}")
      string(REGEX REPLACE ".*#define CEREAL_VERSION_PATCH ([0-9]+).*" "\\1"
          CEREAL_VERSION_PATCH "${_CEREAL_HEADER_CONTENTS}")

    elseif (EXISTS "${CEREAL_INCLUDE_DIR}/cereal/details/polymorphic_impl_fwd.hpp")

      set(CEREAL_VERSION_MAJOR 1)
      set(CEREAL_VERSION_MINOR 2)
      set(CEREAL_VERSION_PATCH 0)
    elseif (EXISTS "${CEREAL_INCLUDE_DIR}/cereal/types/valarray.hpp")

      set(CEREAL_VERSION_MAJOR 1)
      set(CEREAL_VERSION_MINOR 1)
      set(CEREAL_VERSION_PATCH 2)
    elseif (EXISTS "${CEREAL_INCLUDE_DIR}/cereal/cereal.hpp")

    set(CEREAL_VERSION_MAJOR 1)
    set(CEREAL_VERSION_MINOR 1)
    set(CEREAL_VERSION_PATCH 1)
  else()

    set(CEREAL_FOUND NO)
    endif()
    set(CEREAL_VERSION_STRING "${CEREAL_VERSION_MAJOR}.${CEREAL_VERSION_MINOR}.${CEREAL_VERSION_PATCH}")
  endif ()

endmacro()

macro(find_ensmallen)

  set(CURRENT_PATH ${ARGN})
  if (CURRENT_PATH)
    find_path(ENSMALLEN_INCLUDE_DIR
      NAMES ensmallen.hpp
      PATHS "${CURRENT_PATH}/deps/ensmallen/include"
      NO_DEFAULT_PATH)
  else()
    file(GLOB ENSMALLEN_SEARCH_PATHS
        ${CMAKE_BINARY_DIR}/deps/ensmallen-[0-9]*.[0-9]*.[0-9]*)
    find_path(ENSMALLEN_INCLUDE_DIR
      NAMES ensmallen.hpp
      PATHS ${ENSMALLEN_SEARCH_PATHS}/include)
  endif()

  if (ENSMALLEN_INCLUDE_DIR)
    # ------------------------------------------------------------------------
    #  Extract version information from <ensmallen>
    # ------------------------------------------------------------------------

    set(ENSMALLEN_VERSION_MAJOR 0)
    set(ENSMALLEN_VERSION_MINOR 0)
    set(ENSMALLEN_VERSION_PATCH 0)
    set(ENSMALLEN_VERSION_NAME "unknown")

    if(EXISTS "${ENSMALLEN_INCLUDE_DIR}/ensmallen_bits/ens_version.hpp")

      set(ENSMALLEN_FOUND YES)

      # Read and parse Ensmallen version header file for version number
      file(READ "${ENSMALLEN_INCLUDE_DIR}/ensmallen_bits/ens_version.hpp"
          _ensmallen_HEADER_CONTENTS)
      string(REGEX REPLACE ".*#define ENS_VERSION_MAJOR ([0-9]+).*" "\\1"
          ENSMALLEN_VERSION_MAJOR "${_ensmallen_HEADER_CONTENTS}")
      string(REGEX REPLACE ".*#define ENS_VERSION_MINOR ([0-9]+).*" "\\1"
          ENSMALLEN_VERSION_MINOR "${_ensmallen_HEADER_CONTENTS}")
      string(REGEX REPLACE ".*#define ENS_VERSION_PATCH ([0-9]+).*" "\\1"
          ENSMALLEN_VERSION_PATCH "${_ensmallen_HEADER_CONTENTS}")

      # WARNING: The number of spaces before the version name is not one.
      string(REGEX REPLACE
          ".*#define ENS_VERSION_NAME\ +\"([0-9a-zA-Z\ _-]+)\".*" "\\1"
          ENSMALLEN_VERSION_NAME "${_ensmallen_HEADER_CONTENTS}")

    endif()

    set(ENSMALLEN_VERSION_STRING "${ENSMALLEN_VERSION_MAJOR}.${ENSMALLEN_VERSION_MINOR}.${ENSMALLEN_VERSION_PATCH}")
  endif ()
endmacro()

macro(find_stb)
  file(GLOB STB_IMAGE_SEARCH_PATHS
      ${CMAKE_BINARY_DIR}/deps/
      ${CMAKE_BINARY_DIR}/deps/stb)
  find_path(STB_IMAGE_INCLUDE_DIR_1
      NAMES stb_image.h stb_image_write.h stb_image_resize2.h
      PATHS ${STB_IMAGE_SEARCH_PATHS} ${STB_IMAGE_INCLUDE_DIR})

  if(STB_IMAGE_INCLUDE_DIR_1)
    set(STB_IMAGE_INCLUDE_DIR "${STB_IMAGE_INCLUDE_DIR_1}" CACHE PATH
        "stb_image include directory")

    # Either we found /usr/include/stb_image.h (or similar), or the user passed
    # a directory in STB_IMAGE_SEARCH_PATHS that directly contains stb_image.h.
    # In either of those cases, we want to include <stb_image.h>, not
    # <stb/stb_image.h>.
    set(STB_INCLUDE_NEEDS_STB_SUFFIX "NO")
  else ()
    find_path(STB_IMAGE_INCLUDE_DIR_2
          NAMES stb_image.h stb_image_write.h stb_image_resize2.h
          PATHS ${STB_IMAGE_SEARCH_PATHS} ${STB_IMAGE_INCLUDE_DIR}
          PATH_SUFFIXES stb/)

    if (STB_IMAGE_INCLUDE_DIR_2)
      set(STB_IMAGE_INCLUDE_DIR "${STB_IMAGE_INCLUDE_DIR_2}" CACHE PATH
          "stb_image include directory")

      # Since we searched the same paths but allowed an stb/ suffix this time,
      # then there is definitely a suffix.
      set(STB_INCLUDE_NEEDS_STB_SUFFIX "YES")
      # Strip the suffix.
      string(REGEX REPLACE ".*stb[/]?$" "" STB_IMAGE_INCLUDE_DIR
          "${STB_IMAGE_INCLUDE_DIR}")
    endif ()
  endif ()

endmacro()

macro(find_openmp)
  find_package(OpenMP)

  if (OpenMP_FOUND AND OpenMP_CXX_VERSION VERSION_GREATER_EQUAL 3.0.0)
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
    set(MLPACK_LIBRARIES ${MLPACK_LIBRARIES} ${OpenMP_CXX_LIBRARIES})
  else ()
    # Disable warnings for all the unknown OpenMP pragmas.
    if (NOT MSVC)
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas")
    else ()
      set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4068")
    endif ()
    set(OpenMP_CXX_FLAGS "")
  endif ()
endmacro()

macro(find_mlpack_internal)

  set(CURRENT_PATH ${ARGN})

  if (CURRENT_PATH)
    file(GLOB MLPACK_SEARCH_PATHS
      ${CURRENT_PATH}/deps/mlpack-[0-9]*.[0-9]*.[0-9]*/src/)

    list(POP_BACK MLPACK_SEARCH_PATHS MLPACK_SEARCH_PATH)
    if (EXISTS ${MLPACK_SEARCH_PATH}/mlpack.hpp)
      set(MLPACK_INCLUDE_DIR ${MLPACK_SEARCH_PATH})
    endif()

  else()
    file(GLOB MLPACK_SEARCH_PATHS
      ${CMAKE_BINARY_DIR}/deps/mlpack-[0-9]*.[0-9]*.[0-9]*)

    # This will be executed if mlpack is installed already.
    find_path(MLPACK_INCLUDE_DIR
      NAMES mlpack.hpp
      PATHS ${MLPACK_SEARCH_PATHS}/include)

    # This will be executed when compiling mlpack bindings and tests.
    if (NOT MLPACK_INCLUDE_DIR)
      find_path(MLPACK_INCLUDE_DIR
        NAMES mlpack.hpp
        PATHS "${CMAKE_CURRENT_SOURCE_DIR}/src/")
    endif()
  endif()

  if (MLPACK_INCLUDE_DIR)
    # ------------------------------------------------------------------------
    #  Extract version information from <mlpack>
    # ------------------------------------------------------------------------

    set(MLPACK_VERSION_MAJOR 0)
    set(MLPACK_VERSION_MINOR 0)
    set(MLPACK_VERSION_PATCH 0)

    if (EXISTS "${MLPACK_INCLUDE_DIR}/mlpack/core/util/version.hpp")

      set(MLPACK_FOUND YES)

      # Read and parse mlpack version header file for version number
      file(READ "${MLPACK_INCLUDE_DIR}/mlpack/core/util/version.hpp"
          _mlpack_HEADER_CONTENTS)
      string(REGEX REPLACE ".*#define MLPACK_VERSION_MAJOR ([0-9]+).*" "\\1"
          MLPACK_VERSION_MAJOR "${_mlpack_HEADER_CONTENTS}")
      string(REGEX REPLACE ".*#define MLPACK_VERSION_MINOR ([0-9]+).*" "\\1"
          MLPACK_VERSION_MINOR "${_mlpack_HEADER_CONTENTS}")
      string(REGEX REPLACE ".*#define MLPACK_VERSION_PATCH ([0-9]+).*" "\\1"
          MLPACK_VERSION_PATCH "${_mlpack_HEADER_CONTENTS}")

    endif()

    set(MLPACK_VERSION_STRING "${MLPACK_VERSION_MAJOR}.${MLPACK_VERSION_MINOR}.${MLPACK_VERSION_PATCH}")
  endif()

  include(FindPackageHandleStandardArgs)
  find_package_handle_standard_args(mlpack
    REQUIRED_VARS MLPACK_INCLUDE_DIR
    VERSION_VAR MLPACK_VERSION_STRING)

endmacro()

macro(compile_OpenBLAS)
  if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
    set(OPENBLAS_SRC_DIR ${CMAKE_BINARY_DIR}/deps/OpenBLAS-${OPENBLAS_VERSION})
    set(OPENBLAS_BUILD_DIR ${OPENBLAS_SRC_DIR}/build)
    set(OPENBLAS_OUTPUT_LIB_DIR ${OPENBLAS_BUILD_DIR}/lib/Release)
    # always compile BLAS as release.
    set(BLASS_BUILD_TYPE "Release")

    if (NOT EXISTS "${OPENBLAS_OUTPUT_LIB_DIR}/openblas.lib")
      message(STATUS "Compiling OpenBLAS")
      file(MAKE_DIRECTORY ${OPENBLAS_BUILD_DIR})
      # -G -A -T to pass settings from current cmake command.
      execute_process(
              COMMAND ${CMAKE_COMMAND}
              -G "${CMAKE_GENERATOR}"
              -A "${CMAKE_GENERATOR_PLATFORM}"
              -T "${CMAKE_GENERATOR_TOOLSET}"
              "-DCMAKE_BUILD_TYPE=${BLASS_BUILD_TYPE}"
              "-DBUILD_SHARED_LIBS=OFF"
              -S ${OPENBLAS_SRC_DIR} -B ${OPENBLAS_BUILD_DIR}

              WORKING_DIRECTORY ${OPENBLAS_SRC_DIR}
      )
      execute_process(
              COMMAND ${CMAKE_COMMAND} --build ${OPENBLAS_BUILD_DIR}
              --config ${BLASS_BUILD_TYPE} --parallel
              WORKING_DIRECTORY ${OPENBLAS_SRC_DIR}
      )
    else()
      message(STATUS "OpenBLAS is already compiled")
    endif()
    file(GLOB OPENBLAS_LIBRARIES ${OPENBLAS_OUTPUT_LIB_DIR}/openblas.lib)
  else()
    execute_process(COMMAND make NO_SHARED=1 WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/deps/OpenBLAS-${OPENBLAS_VERSION})
    file(GLOB OPENBLAS_LIBRARIES "${CMAKE_BINARY_DIR}/deps/OpenBLAS-${OPENBLAS_VERSION}/libopenblas.a")
  endif()
  set(BLAS_openblas_LIBRARY ${OPENBLAS_LIBRARIES})
  set(LAPACK_openblas_LIBRARY ${OPENBLAS_LIBRARIES})
  set(BLAS_FOUND ON)
endmacro()

macro(fetch_mlpack COMPILE_OPENBLAS)

  if (CMAKE_CROSSCOMPILING)
    search_openblas(${OPENBLAS_VERSION})
    # Set to cross compile openblas if the user forgot to do so.
    set(COMPILE_OPENBLAS ON)
  endif()

  find_package(BLAS PATHS ${CMAKE_BINARY_DIR})
  if (NOT BLAS_FOUND OR (NOT BLAS_LIBRARIES))
    get_deps(https://github.com/xianyi/OpenBLAS/releases/download/v${OPENBLAS_VERSION}/OpenBLAS-${OPENBLAS_VERSION}.tar.gz
        OpenBLAS OpenBLAS-${OPENBLAS_VERSION}.tar.gz)
    if (NOT COMPILE_OPENBLAS)
      message(WARNING "OpenBLAS is downloaded but not compiled. Please compile
      OpenBLAS before compiling mlpack")
    else()
      compile_OpenBLAS()
    endif()
  endif()

  find_armadillo(${CMAKE_BINARY_DIR})
  if (NOT ARMADILLO_FOUND)
    if (NOT CMAKE_CROSSCOMPILING)
      find_package(BLAS QUIET)
      find_package(LAPACK QUIET)
    endif()
    get_deps(https://files.mlpack.org/armadillo-${ARMADILLO_FETCH_VERSION}.tar.gz armadillo armadillo-${ARMADILLO_FETCH_VERSION}.tar.gz)
    set(ARMADILLO_INCLUDE_DIR ${GENERIC_INCLUDE_DIR})
    find_armadillo(${CMAKE_BINARY_DIR})
  endif()
  if (ARMADILLO_FOUND)
    # Include directories for the previous dependencies.
    set(MLPACK_INCLUDE_DIRS ${MLPACK_INCLUDE_DIRS} ${ARMADILLO_INCLUDE_DIRS})
    set(MLPACK_LIBRARIES ${MLPACK_LIBRARIES} ${ARMADILLO_LIBRARIES})
  endif()

  find_ensmallen(${CMAKE_BINARY_DIR})
  if (NOT ENSMALLEN_FOUND)
    get_deps(https://www.ensmallen.org/files/ensmallen-${ENSMALLEN_FETCH_VERSION}.tar.gz ensmallen ensmallen-${ENSMALLEN_FETCH_VERSION}.tar.gz)
    set(ENSMALLEN_INCLUDE_DIR ${GENERIC_INCLUDE_DIR})
    find_ensmallen(${CMAKE_BINARY_DIR})
  endif()
  if (ENSMALLEN_FOUND)
    set(MLPACK_INCLUDE_DIRS ${MLPACK_INCLUDE_DIRS} ${ENSMALLEN_INCLUDE_DIR})
  endif()

  find_cereal(${CMAKE_BINARY_DIR})
  if (NOT CEREAL_FOUND)
    get_deps(https://github.com/USCiLab/cereal/archive/refs/tags/v${CEREAL_FETCH_VERSION}.tar.gz cereal cereal-${CEREAL_FETCH_VERSION}.tar.gz)
    set(CEREAL_INCLUDE_DIR ${GENERIC_INCLUDE_DIR})
    find_cereal(${CMAKE_BINARY_DIR})
  endif()
  if (CEREAL_FOUND)
    set(MLPACK_INCLUDE_DIRS ${MLPACK_INCLUDE_DIRS} ${CEREAL_INCLUDE_DIR})
  endif()

  find_mlpack_internal(${CMAKE_BINARY_DIR})
  if (NOT MLPACK_FOUND)
    get_deps(https://www.mlpack.org/files/mlpack-${MLPACK_FETCH_VERSION}.tar.gz mlpack mlpack-${MLPACK_FETCH_VERSION}.tar.gz)
    set(MLPACK_INCLUDE_DIR ${GENERIC_INCLUDE_DIR})
    find_mlpack_internal(${CMAKE_BINARY_DIR})
  endif()
  if (MLPACK_FOUND)
    set(MLPACK_INCLUDE_DIRS ${MLPACK_INCLUDE_DIRS} ${MLPACK_INCLUDE_DIR})
  endif()

  find_openmp() 

endmacro()

##===================================================
##  MLPACK MAIN FUNCTIONS CALL. 
##===================================================

macro(find_mlpack)
  # If we're using gcc, then we need to link against pthreads to use std::thread,
  # which we do in the tests.
  if (CMAKE_COMPILER_IS_GNUCC)
    find_package(Threads)
    set(MLPACK_LIBRARIES ${MLPACK_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
  endif()

  if (NOT MLPACK_DISABLE_OPENMP)
    find_openmp()
  endif ()

  find_armadillo()
  if (ARMADILLO_FOUND)
    set(MLPACK_INCLUDE_DIRS ${ARMADILLO_INCLUDE_DIRS})
    set(MLPACK_LIBRARIES ${MLPACK_LIBRARIES} ${ARMADILLO_LIBRARIES})
  else()
    message(FATAL_ERROR "Armadillo not found, (required dependency of mlpack).")
  endif ()

  find_ensmallen()
  if (ENSMALLEN_FOUND)
    set(MLPACK_INCLUDE_DIRS ${MLPACK_INCLUDE_DIRS} ${ENSMALLEN_INCLUDE_DIR})
  else()
    message(FATAL_ERROR "Ensmallen not found, (required dependency of mlpack).")
  endif()

  find_cereal()
  if (CEREAL_FOUND)
    set(MLPACK_INCLUDE_DIRS ${MLPACK_INCLUDE_DIRS} ${CEREAL_INCLUDE_DIR})
  else()
    message(FATAL_ERROR "Cereal not found, (required dependency of mlpack).")
  endif()

  if (MLPACK_USE_SYSTEM_STB)
    find_stb()
  endif()
  if (StbImage_FOUND)
    set(STB_AVAILABLE "1")
    add_definitions(-DMLPACK_USE_SYSTEM_STB)
    set(MLPACK_INCLUDE_DIRS ${MLPACK_INCLUDE_DIRS} ${STB_IMAGE_INCLUDE_DIR})
  endif()

  find_mlpack_internal()
  if (MLPACK_FOUND)
    set(MLPACK_INCLUDE_DIRS ${MLPACK_INCLUDE_DIRS} ${MLPACK_INCLUDE_DIR})
  else()
    message(FATAL_ERROR "mlpack not found!")
  endif()

  mark_as_advanced(MLPACK_INCLUDE_DIR)
  mark_as_advanced(MLPACK_INCLUDE_DIRS)
  mark_as_advanced(MLPACK_LIBRARIES)

endmacro()