--!A cross-platform build utility based on Lua
--
-- Licensed 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.
--
-- Copyright (C) 2015-present, Xmake Open Source Community.
--
-- @author      ruki
-- @file        find_iccenv.lua
--

-- imports
import("lib.detect.find_file")
import("lib.detect.find_tool")

-- init icl variables
local iclvars = {"path",
                 "lib",
                 "libpath",
                 "include",
                 "DevEnvdir",
                 "VSInstallDir",
                 "VCInstallDir",
                 "WindowsSdkDir",
                 "WindowsLibPath",
                 "WindowsSDKVersion",
                 "WindowsSdkBinPath",
                 "UniversalCRTSdkDir",
                 "UCRTVersion"}

-- load iclvars_bat environment variables
function _load_iclvars(iclvars_bat, arch, opt)

    -- make the geniclvars.bat
    opt = opt or {}
    local geniclvars_bat = os.tmpfile() .. "_geniclvars.bat"
    local geniclvars_dat = os.tmpfile() .. "_geniclvars.txt"
    local file = io.open(geniclvars_bat, "w")
    file:print("@echo off")
    file:print("call \"%s\" -arch %s > nul", iclvars_bat, arch)
    for idx, var in ipairs(iclvars) do
        file:print("echo " .. var .. " = %%" .. var .. "%% %s %s", idx == 1 and ">" or ">>", geniclvars_dat)
    end
    file:close()

    -- run geniclvars.bat
    -- @note we use runv here so the bat path is not split on whitespace by os.argv,
    -- which breaks detection when the temp file lives under a path with spaces.
    os.runv(geniclvars_bat)

    -- load all envirnoment variables
    local variables = {}
    for _, line in ipairs((io.readfile(geniclvars_dat) or ""):split("\n")) do
        local p = line:find('=', 1, true)
        if p then
            local name = line:sub(1, p - 1):trim()
            local value = line:sub(p + 1):trim()
            variables[name] = value
        end
    end
    if not variables.path then
        return
    end

    -- remove some empty entries
    for _, name in ipairs(iclvars) do
        if variables[name] and #variables[name]:trim() == 0 then
            variables[name] = nil
        end
    end

    -- fix bin path for ia32
    if variables.path and arch == "ia32" then
        variables.path = variables.path:gsub("windows\\bin\\intel64;", "windows\\bin\\intel64_ia32;")
    end

    -- convert path/lib/include to PATH/LIB/INCLUDE
    variables.PATH    = variables.path
    variables.LIB     = variables.lib
    variables.LIBPATH = variables.libpath
    variables.INCLUDE = variables.include
    variables.path    = nil
    variables.lib     = nil
    variables.include = nil
    variables.libpath = nil
    return variables
end

-- find intel c/c++ envirnoment on windows
function _find_intel_on_windows(opt)

    -- init options
    opt = opt or {}

    -- find iclvars_bat.bat
    local paths = {"$(env ICPP_COMPILER20)"}
    local iclvars_bat = find_file("bin/iclvars.bat", paths)
    -- look for setvars.bat which is new in 2021
    if not iclvars_bat then
        -- find setvars.bat in intel oneapi toolkits rootdir
        paths = {"$(env ONEAPI_ROOT)"}
        iclvars_bat = find_file("setvars.bat", paths)
    end
    if not iclvars_bat then
        -- find setvars.bat using ICPP_COMPILER.*
        paths = {
            "$(env ICPP_COMPILER21)",
            "$(env ICPP_COMPILER22)",
            "$(env ICPP_COMPILER23)"
        }
        iclvars_bat = find_file("../../../setvars.bat", paths)
        if not iclvars_bat then
            paths = {
                "$(env ICPP_COMPILER24)"
            }
            iclvars_bat = find_file("../../setvars.bat", paths)
        end
    end
    if iclvars_bat then

        -- load iclvars_bat
        local iclvars_x86 = _load_iclvars(iclvars_bat, "ia32", opt)
        local iclvars_x64 = _load_iclvars(iclvars_bat, "intel64", opt)

        -- save results
        return {iclvars_bat = iclvars_bat, iclvars = {x86 = iclvars_x86, x64 = iclvars_x64}}
     end
end

-- find intel c/c++ envirnoment on linux
function _find_intel_on_linux(opt)

    -- attempt to find the sdk directory
    local paths = {"/opt/intel/bin", "/usr/local/bin", "/usr/bin"}
    local icc = find_file("icc", paths)
    if icc then
        local sdkdir = path.directory(path.directory(icc))
        return {sdkdir = sdkdir, bindir = path.directory(icc), path.join(sdkdir, "include"), libdir = path.join(sdkdir, "lib")}
    end

    -- find it from oneapi sdk directory
    local oneapi_rootdirs = {"~/intel/oneapi/compiler", "/opt/intel/oneapi/compiler"}
    local arch = os.arch() == "x86_64" and "intel64" or "ia32"
    paths = {}
    for _, rootdir in ipairs(oneapi_rootdirs) do
        table.insert(paths, path.join(rootdir, "*", is_host("macosx") and "mac" or "linux", "bin", arch))
    end
    if #paths > 0 then
        local icc = find_file("icc", paths)
        if icc then
            local bindir = path.directory(icc)
            local sdkdir = path.directory(path.directory(bindir))
            return {sdkdir = sdkdir, bindir = bindir, libdir = path.join(sdkdir, "compiler", "lib", arch)}
        end
    end
end

-- find intel c/c++ environment
function main(opt)
    if is_host("windows") then
        return _find_intel_on_windows(opt)
    else
        return _find_intel_on_linux(opt)
    end
end
