blob: fc915526a70a14848d65d16a2f32feee574c6a00 [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
using CMake
using JSON
using Libdl
using LinearAlgebra
################################################################################
# First try to detect and load existing libmxnet
################################################################################
libmxnet_detected = false
libmxnet_curr_ver = get(ENV, "MXNET_COMMIT", "master")
curr_win = "20190608" # v1.5.0
if haskey(ENV, "MXNET_HOME")
MXNET_HOME = ENV["MXNET_HOME"]
@info("MXNET_HOME environment detected: $MXNET_HOME")
@info("Trying to load existing libmxnet...")
# In case of macOS, if user build libmxnet from source and set the MXNET_HOME,
# the output is still named as `libmxnet.so`.
lib = Libdl.find_library(["libmxnet.$(Libdl.dlext)", "libmxnet.so"],
[joinpath(MXNET_HOME, "lib"), MXNET_HOME])
if !isempty(lib)
@info("Existing libmxnet detected at $lib, skip building...")
libmxnet_detected = true
else
@info("Failed to load existing libmxnet, trying to build from source...")
end
end
# Try to find cuda
CUDAPATHS = String[]
if haskey(ENV, "CUDA_HOME")
push!(CUDAPATHS, joinpath(ENV["CUDA_HOME"], "lib64"))
elseif Sys.islinux()
append!(CUDAPATHS, ["/opt/cuda/lib64", "/usr/local/cuda/lib64"])
end
if Sys.isunix()
nvcc_path = Sys.which("nvcc")
if nvcc_path nothing
@info "Found nvcc: $nvcc_path"
push!(CUDAPATHS, replace(nvcc_path, "bin/nvcc" => "lib64"))
end
end
HAS_CUDA = false
HAS_CUDNN = false
let cudalib = Libdl.find_library(["libcuda", "nvcuda.dll"], CUDAPATHS)
global HAS_CUDA = !isempty(cudalib) && Libdl.dlopen_e(cudalib) != C_NULL
end
if !HAS_CUDA && Sys.iswindows()
# TODO: this needs to be improved.
try
run(`nvcc --version`)
global HAS_CUDA = true
catch
end
end
if HAS_CUDA # then check cudnn
let cudnnlib = Libdl.find_library("libcudnn", CUDAPATHS)
global HAS_CUDNN = !isempty(cudnnlib) && Libdl.dlopen_e(cudnnlib) != C_NULL
if HAS_CUDNN && !haskey(ENV, "CUDA_HOME") # inference `CUDA_HOME`
ENV["CUDA_HOME"] = dirname(dirname(cudnnlib))
end
end
end
if HAS_CUDA
@info("Found a CUDA installation.")
if HAS_CUDNN
@info("Found a CuDNN installation.")
end
@info("CUDA_HOME -> $(get(ENV, "CUDA_HOME", "nothing"))")
else
@info("Did not find a CUDA installation, using CPU-only version of MXNet.")
end
# propagate more build flags from ENV
const USE_JEMALLOC = get(ENV, "USE_JEMALLOC", nothing) # "ON" or "OFF"
get_cpucore() = min(Sys.CPU_THREADS, 32)
cmake_bool(x::Bool) = ifelse(x true, "ON", "OFF")
cmake_jemalloc(::Nothing) = ""
cmake_jemalloc(x::Bool) = "-DUSE_JEMALLOC=" * cmake_bool(x)
cmake_cuda_path(::Nothing) = ""
cmake_cuda_path(x::String) = "-DUSE_CUDA_PATH=" * x
cmake_jl_blas(x::Bool, blas_path) = ifelse(x, "-DOpenBLAS_LIB=$blas_path", "")
using BinDeps
@BinDeps.setup
if !libmxnet_detected
if Sys.iswindows()
if Sys.ARCH != :x86_64
@info("Prebuilt windows binaries are only available on 64bit. You will have to built MXNet yourself.")
return
end
@info("Downloading pre-built packages for Windows.")
base_url = "https://github.com/yajiedesign/mxnet/releases/download/weekly_binary_build_v2/prebuildbase_win10_x64_vc14_v2.7z"
if libmxnet_curr_ver == "master"
_cmd = "{
[System.Net.ServicePointManager]::SecurityProtocol='tls12';
Invoke-WebRequest -Uri 'https://api.github.com/repos/yajiedesign/mxnet/releases/latest'
-OutFile 'mxnet.json'}"
# download_cmd uses powershell 2, but we need powershell 3 to do this
run(`powershell -NoProfile -Command $_cmd`)
curr_win = JSON.parsefile("mxnet.json")["tag_name"]
@info("Can't use MXNet master on Windows, using latest binaries from $curr_win.")
end
# TODO: Get url from JSON.
# TODO: detect cuda version and select corresponding url.
name = "mxnet_x64_$(HAS_CUDA ? "vc141_gpu_cu101" : "vc14_cpu").7z"
package_url = "https://github.com/yajiedesign/mxnet/releases/download/$(curr_win)/$(curr_win)_$(name)"
exe7z = joinpath(Sys.BINDIR, "7z.exe")
run(download_cmd(package_url, "mxnet.7z"))
# this command will create the dir "usr\\lib"
run(`$exe7z e mxnet.7z "*\\build\\*" "*\\lib\\*" -y -ousr\\lib`) # TODO check it works on windows or not
run(download_cmd(base_url, "mxnet_base.7z"))
run(`$exe7z x mxnet_base.7z -y -ousr`)
run(`cmd /c copy "usr\\prebuildbase_win10_x64_vc14_v2\\3rdparty\\bin\\*.dll" "usr\\lib"`)
# testing
run(`cmd /c dir "usr\\lib"`)
return
end # if Sys.iswindows()
################################################################################
# If not found, try to build automatically using BinDeps
################################################################################
blas_path = Libdl.dlpath(Libdl.dlopen(Base.libblas_name))
blas_vendor = LinearAlgebra.BLAS.vendor()
USE_JULIA_BLAS = (blas_vendor in (:openblas, :openblas64))
@info "USE_JULIA_BLAS -> $USE_JULIA_BLAS"
blas_name = occursin("openblas", string(blas_vendor)) ? "open" : string(blas_vendor)
#--------------------------------------------------------------------------------
# Build libmxnet
mxnet = library_dependency("mxnet", aliases=["mxnet", "libmxnet", "libmxnet.so"])
_blddir = joinpath(BinDeps.depsdir(mxnet), "build")
_srcdir = joinpath(BinDeps.depsdir(mxnet), "src")
_mxdir = joinpath(_srcdir, "mxnet")
# We have to eagerly delete the build directory.
# Otherwise we won't rebuild on an update.
rm(_blddir, recursive=true, force=true)
@debug "build dir -> $_blddir"
provides(BuildProcess,
(@build_steps begin
CreateDirectory(_blddir)
CreateDirectory(_srcdir)
@build_steps begin
BinDeps.DirectoryRule(_mxdir, @build_steps begin
ChangeDirectory(_srcdir)
`git clone --recursive https://github.com/apache/incubator-mxnet mxnet`
end)
@build_steps begin
ChangeDirectory(_mxdir)
`git fetch`
`git submodule update --recursive --force`
if libmxnet_curr_ver != "master"
`git checkout $libmxnet_curr_ver`
else
`git checkout origin/$libmxnet_curr_ver`
end
`cp -f -v julia/deps/include/cblas.h include/cblas.h`
end
@build_steps begin
ChangeDirectory(_blddir)
`$cmake
-DCMAKE_BUILD_TYPE=$(libmxnet_curr_ver == "master" ? "Debug" : "Release")
-DUSE_BLAS=$blas_name
-DUSE_OPENCV=$(cmake_bool(false))
-DUSE_CUDA=$(cmake_bool(HAS_CUDA))
-DUSE_CUDNN=$(cmake_bool(HAS_CUDNN))
$(cmake_jemalloc(USE_JEMALLOC))
$(cmake_cuda_path(get(ENV, "CUDA_HOME", nothing)))
$(cmake_jl_blas(USE_JULIA_BLAS, blas_path))
$_mxdir`
`make -j$(get_cpucore()) VERBOSE=$(Int(libmxnet_curr_ver == "master"))`
end
FileRule(joinpath(_blddir, "libmxnet.$(Libdl.dlext)"), @build_steps begin
# the output file on macos is still in `.so` suffix,
# so we create a soft link for it.
`ln -s libmxnet.so $_blddir/libmxnet.$(Libdl.dlext)`
end)
end
end), mxnet, installed_libpath=_blddir)
@BinDeps.install Dict(:mxnet => :mxnet)
end