--!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 clang_cl.lua -- -- inherit cl inherit("cl") import("core.base.option") import("core.base.tty") import("core.base.colors") import("core.project.policy") import("utils.progress") -- init it function init(self) -- init flags map self:set("mapflags", { -- warnings ["-W1"] = "-Wall" , ["-W2"] = "-Wall" , ["-W3"] = "-Wall" , ["-W4"] = "-Wall -Wextra" , ["-Weverything"] = "-Wall -Wextra -Weffc++" -- language , ["-ansi"] = "-Xclang -ansi" , ["-std=c89"] = "-Xclang -std=c89" , ["-std=c99"] = "-Xclang -std=c99" , ["-std=c11"] = "-Xclang -std=c11" , ["-std=gnu89"] = "-Xclang -std=gnu89" , ["-std=gnu99"] = "-Xclang -std=gnu99" , ["-std=gnu11"] = "-Xclang -std=gnu11" , ["-std=c++98"] = "-Xclang -std=c++98" , ["-std=c++11"] = "-Xclang -std=c++11" , ["-std=c++14"] = "-Xclang -std=c++14" , ["-std=c++17"] = "-Xclang -std=c++17" , ["-std=c++1z"] = "-Xclang -std=c++1z" , ["-std=c++1a"] = "-Xclang -std=c++1a" , ["-std=c++20"] = "-Xclang -std=c++20" , ["-std=c++2a"] = "-Xclang -std=c++2a" , ["-std=c++23"] = "-Xclang -std=c++23" , ["-std=c++2b"] = "-Xclang -std=c++2b" , ["-std=gnu++98"] = "-Xclang -std=gnu++98" , ["-std=gnu++11"] = "-Xclang -std=gnu++11" , ["-std=gnu++14"] = "-Xclang -std=gnu++14" , ["-std=gnu++17"] = "-Xclang -std=gnu++17" , ["-std=gnu++1z"] = "-Xclang -std=gnu++1z" , ["-std=gnu++1a"] = "-Xclang -std=gnu++1a" , ["-std=gnu++20"] = "-Xclang -std=gnu++20" , ["-std=gnu++2a"] = "-Xclang -std=gnu++2a" , ["-std=gnu++23"] = "-Xclang -std=gnu++23" , ["-std=gnu++2b"] = "-Xclang -std=gnu++2b" }) end -- has color diagnostics? function _has_color_diagnostics(self) local colors_diagnostics = _g._HAS_COLOR_DIAGNOSTICS if colors_diagnostics == nil then if io.isatty() and (tty.has_color8() or tty.has_color256()) then local theme = colors.theme() if theme and theme:name() ~= "plain" then -- for clang if self:has_flags("-fcolor-diagnostics", "cxflags") then colors_diagnostics = "-fcolor-diagnostics" -- for gcc elseif self:has_flags("-fdiagnostics-color=always", "cxflags") then colors_diagnostics = "-fdiagnostics-color=always" end -- enable color output for windows, @see https://github.com/xmake-io/xmake-vscode/discussions/260 if colors_diagnostics and self:has_flags("-fansi-escape-codes", "cxflags") then colors_diagnostics = table.join(colors_diagnostics, "-fansi-escape-codes") end end end colors_diagnostics = colors_diagnostics or false _g._HAS_COLOR_DIAGNOSTICS = colors_diagnostics end return colors_diagnostics end -- make the optimize flag function nf_optimize(self, level) local maps = { none = "-Od" , faster = "-Ox" , fastest = "-O2" , smallest = "-O1" , aggressive = "-O2" } return maps[level] end -- make the c precompiled header flag function nf_pcheader(self, pcheaderfile, opt) local target = opt.target if self:kind() == "cc" then local objectfiles = target:objectfiles() if objectfiles then table.insert(objectfiles, target:pcoutputfile("c") .. ".obj") end return {"-Yu" .. path.filename(pcheaderfile), "-FI" .. path.filename(pcheaderfile), "-I" .. path.directory(pcheaderfile), "-Fp" .. target:pcoutputfile("c")} end end -- make the c++ precompiled header flag function nf_pcxxheader(self, pcheaderfile, opt) local target = opt.target if self:kind() == "cxx" then local objectfiles = target:objectfiles() if objectfiles then table.insert(objectfiles, target:pcoutputfile("cxx") .. ".obj") end -- https://github.com/xmake-io/xmake/issues/3905 -- clang-cl need extra include search path return {"-Yu" .. path.filename(pcheaderfile), "-FI" .. path.filename(pcheaderfile), "-I" .. path.directory(pcheaderfile), "-Fp" .. target:pcoutputfile("cxx")} end end -- has /clang:-MMD -MF depfile? function _has_clang_mmd(self) local has_clang_mmd = _g._HAS_CLANG_MMD if has_clang_mmd == nil then local depfile = os.tmpfile() if self:has_flags({"/clang:-MMD", "/clang:-MF", "/clang:" .. depfile}, "cxflags", {flagskey = "clang_mmd"}) then has_clang_mmd = true end has_clang_mmd = has_clang_mmd or false _g._HAS_CLANG_MMD = has_clang_mmd os.tryrm(depfile) end return has_clang_mmd end -- compile the source file function compile(self, sourcefile, objectfile, dependinfo, flags, opt) -- ensure the object directory os.mkdir(path.directory(objectfile)) -- compile it local depfile local depfile_format local outdata = try { function () -- generate includes file local compflags = flags if dependinfo then if _has_clang_mmd(self) then depfile = os.tmpfile() compflags = table.join(flags, "/clang:-MMD", "/clang:-MF", "/clang:" .. depfile) depfile_format = "gcc" else compflags = table.join(flags, "-showIncludes") depfile_format = "cl" end end -- has color diagnostics? enable it local colors_diagnostics = _has_color_diagnostics(self) if colors_diagnostics then compflags = table.join(compflags, colors_diagnostics) end -- do compile local program, argv = compargv(self, sourcefile, objectfile, compflags) return os.iorunv(program, argv, {envs = self:runenvs()}) end, catch { function (errors) -- try removing the old object file for forcing to rebuild this source file os.tryrm(objectfile) if depfile then os.tryrm(depfile) end -- parse and strip errors local lines = errors and tostring(errors):split('\n', {plain = true}) or {} if not option.get("verbose") then -- find the start line of error local start = 0 for index, line in ipairs(lines) do if line:find("error:", 1, true) or line:find("错误:", 1, true) then start = index break end end -- get 16 lines of errors if start > 0 then lines = table.slice(lines, start, start + ((#lines - start > 16) and 16 or (#lines - start))) end end -- raise compiling errors raise(#lines > 0 and table.concat(lines, "\n") or "") end }, finally { function (ok, outdata, errdata) -- show warnings? if ok and errdata and #errdata > 0 and policy.build_warnings(opt) then local lines = errdata:split('\n', {plain = true}) if #lines > 0 then local warnings = table.concat(table.slice(lines, 1, (#lines > 8 and 8 or #lines)), "\n") progress.show_output("${color.warning}%s", warnings) end end end } } -- generate the dependent includes if dependinfo and outdata then if depfile then if os.isfile(depfile) then dependinfo.depfiles_format = depfile_format dependinfo.depfiles = io.readfile(depfile) os.tryrm(depfile) end elseif depfile_format == "cl" then dependinfo.depfiles_format = "cl" dependinfo.depfiles = outdata end end end