diff options
| author | ruki <[email protected]> | 2025-12-03 13:31:02 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-12-03 13:31:02 +0800 |
| commit | 6d803e27d205bc7282597ac4db2a105f2a79d5ad (patch) | |
| tree | 9ce6f4699d63cd4a25f06cb06bdfd40ed36b5fdf | |
| parent | 751be37577faff80964b9e2965e8e271a1717c25 (diff) | |
| parent | 11e5143150ac817eea0ff563207e9774779897ef (diff) | |
Merge pull request #7096 from xmake-io/flang
Add Flang support
| -rw-r--r-- | README.md | 1 | ||||
| -rw-r--r-- | README_zh.md | 1 | ||||
| -rw-r--r-- | xmake/modules/core/tools/flang.lua | 128 | ||||
| -rw-r--r-- | xmake/modules/core/tools/flang/has_flags.lua | 116 | ||||
| -rw-r--r-- | xmake/modules/detect/tools/find_flang.lua | 59 | ||||
| -rw-r--r-- | xmake/toolchains/flang/xmake.lua | 75 |
6 files changed, 380 insertions, 0 deletions
@@ -216,6 +216,7 @@ dmd D Programming Language Compiler ldc The LLVM-based D Compiler gdc The GNU D Compiler (GDC) gfortran GNU Fortran Programming Language Compiler +flang LLVM Fortran Compiler zig Zig Programming Language Compiler sdcc Small Device C Compiler cuda CUDA Toolkit (nvcc, nvc, nvc++, nvfortran) diff --git a/README_zh.md b/README_zh.md index bd9399bfe..77441aade 100644 --- a/README_zh.md +++ b/README_zh.md @@ -277,6 +277,7 @@ dmd D Programming Language Compiler ldc The LLVM-based D Compiler gdc The GNU D Compiler (GDC) gfortran GNU Fortran Programming Language Compiler +flang LLVM Fortran Compiler zig Zig Programming Language Compiler sdcc Small Device C Compiler cuda CUDA Toolkit (nvcc, nvc, nvc++, nvfortran) diff --git a/xmake/modules/core/tools/flang.lua b/xmake/modules/core/tools/flang.lua new file mode 100644 index 000000000..17606b0be --- /dev/null +++ b/xmake/modules/core/tools/flang.lua @@ -0,0 +1,128 @@ +--!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 flang.lua +-- + +-- inherit clang (flang is part of LLVM) +inherit("clang") +import("core.base.option") +import("core.language.language") + +-- init it +function init(self) + + -- init super + _super.init(self) + + -- init shflags + self:set("fcshflags", "-shared") + + -- add -fPIC for shared + if not self:is_plat("windows", "mingw") then + self:add("fcshflags", "-fPIC") + self:add("shared.fcflags", "-fPIC") + end + + -- init flags map to filter unsupported C/C++ flags + self:set("mapflags", + { + -- visibility flags (not supported by Fortran) + ["-fvisibility=.*"] = "", + -- strip flags (not supported by flang, use nf_strip instead) + ["^-s$"] = "" + }) +end + +-- make the strip flag +-- flang doesn't support -s directly, use -Wl,-s for linker +function nf_strip(self, level) + local maps = { + debug = "-Wl,-S" + , all = "-Wl,-s" + } + if self:is_plat("macosx", "iphoneos", "watchos", "appletvos", "applexros") then + maps.all = {"-Wl,-x", "-Wl,-dead_strip"} + elseif self:is_plat("windows") then + -- flang doesn't support strip on windows + maps = {} + end + return maps[level] +end + +-- make the symbol flag +-- Fortran doesn't support visibility flags, only debug symbols +function nf_symbol(self, level) + local kind = self:kind() + if language.sourcekinds()[kind] then + local maps = { + debug = "-g" + } + return maps[level] + elseif kind == "ld" or kind == "sh" then + -- we need to add `-g` to linker to generate pdb symbol file for clang on windows + if level == "debug" and self:is_plat("windows") then + return "-g" + end + end +end + +-- make the link arguments list +-- flang needs to pass install_name through -Wl, for macOS shared libraries +function linkargv(self, objectfiles, targetkind, targetfile, flags, opt) + opt = opt or {} + + -- add install_name for macOS shared libraries + local flags_extra = {} + if targetkind == "shared" and self:is_plat("macosx", "iphoneos", "watchos") then + table.insert(flags_extra, "-Wl,-install_name") + table.insert(flags_extra, "-Wl,@rpath/" .. path.filename(targetfile)) + end + + -- init arguments + local argv = table.join("-o", targetfile, objectfiles, flags, flags_extra) + if is_host("windows") and not opt.rawargs then + argv = winos.cmdargv(argv, {escape = true}) + end + return self:program(), argv +end + +-- link the target file +-- +-- maybe we need to use os.vrunv() to show link output when enable verbose information +-- @see https://github.com/xmake-io/xmake/discussions/2916 +-- +function link(self, objectfiles, targetkind, targetfile, flags, opt) + opt = opt or {} + + -- enable linker output? + local target = opt.target + local linker_output = option.get("verbose") + if linker_output == nil and target and target.policy and target:policy("build.linker.output") then + linker_output = true + end + + os.mkdir(path.directory(targetfile)) + + local program, argv = linkargv(self, objectfiles, targetkind, targetfile, flags, opt) + if linker_output then + os.execv(program, argv, {envs = self:runenvs(), shell = opt.shell}) + else + os.vrunv(program, argv, {envs = self:runenvs(), shell = opt.shell}) + end +end + diff --git a/xmake/modules/core/tools/flang/has_flags.lua b/xmake/modules/core/tools/flang/has_flags.lua new file mode 100644 index 000000000..68f6ad726 --- /dev/null +++ b/xmake/modules/core/tools/flang/has_flags.lua @@ -0,0 +1,116 @@ +--!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 has_flags.lua +-- + +-- imports +import("core.cache.detectcache") +import("core.language.language") + +-- is linker? +function _islinker(flags, opt) + + -- the flags is "-Wl,<arg>" or "-Xlinker <arg>"? + local flags_str = table.concat(flags, " ") + if flags_str:startswith("-Wl,") or flags_str:startswith("-Xlinker ") then + return true + end + + -- the tool kind is ld or sh? + local toolkind = opt.toolkind or "" + return toolkind == "ld" or toolkind == "sh" or toolkind:endswith("-ld") or toolkind:endswith("-sh") +end + +-- try running +function _try_running(program, argv, opt) + local errors = nil + return try { function () os.runv(program, argv, opt); return true end, catch { function (errs) errors = (errs or ""):trim() end }}, errors +end + +-- attempt to check it from the argument list +function _check_from_arglist(flags, opt, islinker) + + -- only for compiler + if islinker or #flags > 1 then + return + end + + -- make cache key + local key = "core.tools.flang.has_flags" + + -- make flags key + local flagskey = opt.program .. "_" .. (opt.programver or "") + + -- get all flags from argument list + local allflags = detectcache:get2(key, flagskey) + if not allflags then + + -- get argument list + allflags = {} + local arglist = os.iorunv(opt.program, {"--help"}, {envs = opt.envs}) + if arglist then + for arg in arglist:gmatch("%s+(%-[%-%a%d]+)%s+") do + allflags[arg] = true + end + end + + -- save cache + detectcache:set2(key, flagskey, allflags) + end + return allflags[flags[1]] +end + +-- try running to check flags +function _check_try_running(flags, opt, islinker) + + -- make an stub source file + local sourcefile = path.join(os.tmpdir(), "detect", "flang_has_flags.f90") + if not os.isfile(sourcefile) then + io.writefile(sourcefile, "program hello\n print *, \"Hello World!\"\nend program hello") + end + + -- check flags for linker + if islinker then + return _try_running(opt.program, table.join(flags, "-o", os.tmpfile(), sourcefile), opt) + end + + -- check flags for compiler + -- @note we cannot use os.nuldev() as the output file, maybe run failed for some flags, e.g. --coverage + return _try_running(opt.program, table.join(flags, "-S", "-o", os.tmpfile(), sourcefile), opt) +end + +-- has_flags(flags)? +-- +-- @param opt the argument options, e.g. {toolname = "", program = "", programver = "", toolkind = "[fc|fcld|fcsh]"} +-- +-- @return true or false +-- +function main(flags, opt) + + -- is linker? + local islinker = _islinker(flags, opt) + + -- attempt to check it from the argument list + if _check_from_arglist(flags, opt, islinker) then + return true + end + + -- try running to check it + return _check_try_running(flags, opt, islinker) +end + diff --git a/xmake/modules/detect/tools/find_flang.lua b/xmake/modules/detect/tools/find_flang.lua new file mode 100644 index 000000000..da21f1490 --- /dev/null +++ b/xmake/modules/detect/tools/find_flang.lua @@ -0,0 +1,59 @@ +--!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_flang.lua +-- + +-- imports +import("lib.detect.find_program") +import("lib.detect.find_programver") + +-- find flang +-- +-- @param opt the argument options, e.g. {version = true} +-- +-- @return program, version +-- +-- @code +-- +-- local flang = find_flang() +-- local flang, version = find_flang({program = "flang", version = true}) +-- +-- @endcode +-- +function main(opt) + + -- init options + opt = opt or {} + + -- find program + local program = find_program(opt.program or "flang", opt) + if not program and is_host("linux") then + -- on Linux, flang might be named as flang-new + program = find_program("flang-new", opt) + end + + -- find program version + local version = nil + if program and opt and opt.version then + version = find_programver(program, opt) + end + + -- ok? + return program, version +end + diff --git a/xmake/toolchains/flang/xmake.lua b/xmake/toolchains/flang/xmake.lua new file mode 100644 index 000000000..86f08c660 --- /dev/null +++ b/xmake/toolchains/flang/xmake.lua @@ -0,0 +1,75 @@ +--!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 xmake.lua +-- + +-- define toolchain +toolchain("flang") + + -- set homepage + set_homepage("https://flang.llvm.org/") + set_description("LLVM Fortran Compiler") + + -- mark as standalone toolchain + set_kind("standalone") + + -- set toolset + set_toolset("fc", "$(env FC)", "flang") + set_toolset("fcld", "$(env FC)", "flang") + set_toolset("fcsh", "$(env FC)", "flang") + set_toolset("ar", "llvm-ar", "ar") + + -- on check + on_check(function (toolchain) + return import("lib.detect.find_tool")("flang", {program = "flang"}) + end) + + -- on load + on_load(function (toolchain) + -- flang doesn't support -m64/-m32, use --target instead (especially on Linux) + local target + if toolchain:is_arch("x86_64", "x64") then + target = "x86_64" + elseif toolchain:is_arch("i386", "x86", "i686") then + target = "i686" + elseif toolchain:is_arch("arm64", "aarch64") then + target = "aarch64" + elseif toolchain:is_arch("arm") then + target = "armv7" + end + + if target then + -- add target suffix based on platform + if toolchain:is_plat("linux") then + target = target .. "-linux-gnu" + elseif toolchain:is_plat("windows") then + target = target .. "-windows-msvc" + elseif toolchain:is_plat("mingw") then + target = target .. "-w64-windows-gnu" + end + + -- only add --target on platforms that need it (Linux, Windows) + -- macOS flang may work without explicit target + if toolchain:is_plat("linux", "windows", "mingw") then + toolchain:add("fcflags", "--target=" .. target) + toolchain:add("fcshflags", "--target=" .. target) + toolchain:add("fcldflags", "--target=" .. target) + end + end + end) + |
