--!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 check_fcsnippets.lua -- -- imports import("core.base.option") import("core.tool.linker") import("core.tool.compiler") -- make source code function _sourcecode(snippets, opt) local sourcecode = "" for _, snippet in pairs(snippets) do sourcecode = sourcecode .. "\n" .. snippet end sourcecode = sourcecode .. "\n" return sourcecode end -- check the given fortran snippets? -- -- @param snippets the snippets -- @param opt the argument options -- e.g. -- { verbose = false, target = [target|option], linkerkind = "fc", "cxx" -- , configs = {defines = "xx", fcflags = ""} -- , tryrun = true, output = true} -- -- funcs: -- sigsetjmp -- sigsetjmp((void*)0, 0) -- sigsetjmp{sigsetjmp((void*)0, 0);} -- sigsetjmp{int a = 0; sigsetjmp((void*)a, a);} -- -- @return true or false -- -- @code -- local ok, output_or_errors = check_fcsnippets([[ -- program hello -- print *, "Hello World!" -- end program hello -- ]], {tryrun = true}) -- @endcode -- function main(snippets, opt) opt = opt or {} snippets = snippets or {} -- get configs local configs = opt.configs or opt.config -- get links local links = {} local target = opt.target if configs and configs.links then table.join2(links, configs.links) end if target and target:type() ~= "package" then table.join2(links, target:get("links")) end if configs and configs.syslinks then table.join2(links, configs.syslinks) end if target and target:type() ~= "package" then table.join2(links, opt.target:get("syslinks")) end -- make source code local sourcecode = _sourcecode(snippets, opt) -- make the source file -- @note we use fixed temporary filenames in order to better cache the compilation results for build_cache. local tmpfile = os.tmpfile(sourcecode) local sourcefile = tmpfile .. ".f90" local objectfile = os.tmpfile() .. ".o" local binaryfile = objectfile:gsub("%.o$", ".b") if not os.isfile(sourcefile) then io.writefile(sourcefile, sourcecode) end -- @note cannot cache result, all conditions will be changed -- attempt to compile it local errors = nil local ok, output = try { function () if option.get("diagnosis") then cprint("${dim}> %s", compiler.compcmd(sourcefile, objectfile, opt)) end opt = table.clone(opt) opt.build_warnings = false compiler.compile(sourcefile, objectfile, opt) local linkerkind = opt.linkerkind or "fc" if #links > 0 or opt.tryrun or opt.binary_match then if option.get("diagnosis") then cprint("${dim}> %s", linker.linkcmd("binary", linkerkind, objectfile, binaryfile, opt)) end linker.link("binary", linkerkind, objectfile, binaryfile, opt) end if opt.tryrun then -- @note we use the *v variants so the binary path is not split on -- whitespace by os.argv. mostly hits on Windows where TEMP lives -- under the user profile and may contain spaces. if opt.output then local output = os.iorunv(binaryfile) if output then output = output:trim() end return true, output else os.vrunv(binaryfile) end end local binary_match = opt.binary_match if binary_match then local content = io.readfile(binaryfile, {encoding = "binary"}) local match = type(binary_match) == "function" and binary_match(content) or content:match(binary_match) if match ~= nil then return true, match end end return true end, catch { function (errs) errors = errs end } } -- remove some files os.tryrm(objectfile) os.tryrm(binaryfile) -- trace if opt.verbose or option.get("verbose") or option.get("diagnosis") then if #links > 0 then cprint("${dim}> checking for fortran links(%s)", table.concat(links, ", ")) end for idx_or_name, snippet in pairs(snippets) do local name = idx_or_name if type(name) == "number" then name = snippet:sub(1, 16) end cprint("${dim}> checking for fortran snippet(%s)", name) end end if errors and option.get("diagnosis") and #tostring(errors) > 0 then cprint("${color.warning}checkinfo:${clear dim} %s", errors) end return ok, ok and output or errors end