summaryrefslogtreecommitdiff
path: root/xmake/rules/c++/modules
diff options
context:
space:
mode:
authorÂngelo Andrade Cirino <[email protected]>2022-01-10 10:23:17 -0300
committerÂngelo Andrade Cirino <[email protected]>2022-01-10 10:23:17 -0300
commited0c0c5e0249aa966ea7061b30710abdd0b09d87 (patch)
tree2a51c869a3aea1a093ed569e749bdf0465634a6c /xmake/rules/c++/modules
parent885d00da8caf74aeed758d030b9a1b50d9078e1f (diff)
parent2431dd7e6142ff98b55741cfd4de4d2e69f4f8b5 (diff)
Merged from upstream/master
Diffstat (limited to 'xmake/rules/c++/modules')
-rw-r--r--xmake/rules/c++/modules/build_modulefiles.lua151
-rw-r--r--xmake/rules/c++/modules/build_modules/clang.lua139
-rw-r--r--xmake/rules/c++/modules/build_modules/gcc.lua96
-rw-r--r--xmake/rules/c++/modules/build_modules/module_parser.lua118
-rw-r--r--xmake/rules/c++/modules/build_modules/msvc.lua181
-rw-r--r--xmake/rules/c++/modules/xmake.lua40
6 files changed, 573 insertions, 152 deletions
diff --git a/xmake/rules/c++/modules/build_modulefiles.lua b/xmake/rules/c++/modules/build_modulefiles.lua
deleted file mode 100644
index 75593f0cc..000000000
--- a/xmake/rules/c++/modules/build_modulefiles.lua
+++ /dev/null
@@ -1,151 +0,0 @@
---!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, TBOOX Open Source Group.
---
--- @author ruki
--- @file build_modulefiles.lua
---
-
--- imports
-import("core.tool.compiler")
-
--- build module files using clang
-function _build_modulefiles_clang(target, sourcebatch, opt)
-
- -- attempt to compile the module files as cxx
- sourcebatch.sourcekind = "cxx"
- sourcebatch.objectfiles = sourcebatch.objectfiles or {}
- sourcebatch.dependfiles = sourcebatch.dependfiles or {}
- for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
- local objectfile = target:objectfile(sourcefile) .. ".pcm"
- table.insert(sourcebatch.objectfiles, objectfile)
- table.insert(sourcebatch.dependfiles, target:dependfile(objectfile))
- end
-
- -- compile module files to *.pcm
- opt = table.join(opt, {configs = {force = {cxxflags = {opt.modulesflag, "--precompile", "-x c++-module"}}}})
- import("private.action.build.object").build(target, sourcebatch, opt)
-
- -- compile *.pcm to object files
- local modulefiles = {}
- for idx, sourcefile in ipairs(sourcebatch.sourcefiles) do
- local modulefile = sourcebatch.objectfiles[idx]
- local objectfile = target:objectfile(sourcefile)
- sourcebatch.sourcefiles[idx] = modulefile
- sourcebatch.objectfiles[idx] = objectfile
- sourcebatch.dependfiles[idx] = target:dependfile(objectfile)
- table.insert(modulefiles, modulefile)
- end
- opt.configs = {cxxflags = {opt.modulesflag}}
- opt.quiet = true
- import("private.action.build.object").build(target, sourcebatch, opt)
-
- -- add module files
- target:add("cxxflags", opt.modulesflag)
- for _, modulefile in ipairs(modulefiles) do
- target:add("cxxflags", "-fmodules", "-fimplicit-modules", "-fimplicit-module-maps", "-fmodule-file=" .. modulefile)
- end
-end
-
--- TODO
--- build module files using gcc
-function _build_modulefiles_gcc(target, sourcebatch, opt)
-
- -- attempt to compile the module files as cxx
- local modulefiles = {}
- opt = table.join(opt, {configs = {}})
- sourcebatch.sourcekind = "cxx"
- sourcebatch.objectfiles = sourcebatch.objectfiles or {}
- sourcebatch.dependfiles = sourcebatch.dependfiles or {}
- for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
- local objectfile = target:objectfile(sourcefile)
- local dependfile = target:dependfile(objectfile)
- local modulefile = objectfile .. ".pcm"
-
- -- compile module file to *.pcm
- local singlebatch = {sourcekind = "cxx", sourcefiles = {sourcefile}, objectfiles = {objectfile}, dependfiles = {dependfile}}
- opt.configs.cxxflags = {"-fmodules-ts", "-x c++"}
- import("private.action.build.object").build(target, singlebatch, opt)
- table.insert(modulefiles, modulefile)
- table.insert(sourcebatch.objectfiles, objectfile)
- table.insert(sourcebatch.dependfiles, dependfile)
- end
-
- -- add module files
- for _, modulefile in ipairs(modulefiles) do
- target:add("cxxflags", "-fmodules-ts", "-fmodule-file=" .. modulefile)
- end
-end
-
--- build module files using msvc
-function _build_modulefiles_msvc(target, sourcebatch, opt)
-
- -- attempt to compile the module files as cxx
- local modulefiles = {}
- opt = table.join(opt, {configs = {}})
- sourcebatch.sourcekind = "cxx"
- sourcebatch.objectfiles = sourcebatch.objectfiles or {}
- sourcebatch.dependfiles = sourcebatch.dependfiles or {}
- for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
- local objectfile = target:objectfile(sourcefile)
- local dependfile = target:dependfile(objectfile)
- local modulefile = objectfile .. ".pcm"
-
- -- compile module file to *.pcm
- local singlebatch = {sourcekind = "cxx", sourcefiles = {sourcefile}, objectfiles = {objectfile}, dependfiles = {dependfile}}
- opt.configs.cxxflags = {"/experimental:module /module:interface /module:output " .. os.args(modulefile), "/TP"}
- import("private.action.build.object").build(target, singlebatch, opt)
- table.insert(modulefiles, modulefile)
- table.insert(sourcebatch.objectfiles, objectfile)
- table.insert(sourcebatch.dependfiles, dependfile)
- end
-
- -- add module files
- for _, modulefile in ipairs(modulefiles) do
- target:add("cxxflags", "/experimental:module /module:reference " .. os.args(modulefile))
- end
-end
-
--- build module files
-function main(target, sourcebatch, opt)
-
- -- do compile
- local modulesflag = nil
- local toolname = target:tool("cxx")
- local compinst = compiler.load("cxx")
- if toolname:find("clang", 1, true) or toolname:find("gcc", 1, true) or toolname:find("g++", 1, true) then
- if compinst:has_flags("-fmodules") then
- modulesflag = "-fmodules"
- elseif compinst:has_flags("-fmodules-ts") then
- modulesflag = "-fmodules-ts"
- end
- elseif toolname == "cl" then
- if compinst:has_flags("/experimental:module") then
- modulesflag = "/experimental:module"
- end
- end
- if modulesflag then
- opt.modulesflag = modulesflag
- if toolname:find("clang", 1, true) then
- _build_modulefiles_clang(target, sourcebatch, opt)
- elseif toolname:find("gcc", 1, true) or toolname:find("g++", 1, true) then
- _build_modulefiles_gcc(target, sourcebatch, opt)
- elseif toolname == "cl" then
- _build_modulefiles_msvc(target, sourcebatch, opt)
- else
- raise("compiler(%s): does not support c++ module!", toolname)
- end
- end
-end
diff --git a/xmake/rules/c++/modules/build_modules/clang.lua b/xmake/rules/c++/modules/build_modules/clang.lua
new file mode 100644
index 000000000..523c61ad9
--- /dev/null
+++ b/xmake/rules/c++/modules/build_modules/clang.lua
@@ -0,0 +1,139 @@
+--!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, TBOOX Open Source Group.
+--
+-- @author ruki
+-- @file clang.lua
+--
+
+-- imports
+import("core.tool.compiler")
+import("private.action.build.object", {alias = "objectbuilder"})
+import("module_parser")
+
+-- load parent target with modules files
+function load_parent(target, opt)
+ -- get modules flag
+ local modulesflag
+ local compinst = compiler.load("cxx", {target = target})
+ if compinst:has_flags("-fmodules") then
+ modulesflag = "-fmodules"
+ elseif compinst:has_flags("-fmodules-ts") then
+ modulesflag = "-fmodules-ts"
+ end
+ assert(modulesflag, "compiler(clang): does not support c++ module!")
+
+ -- add module flags
+ target:add("cxxflags", modulesflag)
+
+ -- the module cache directory
+ for _, dep in ipairs(target:orderdeps()) do
+ local sourcebatches = dep:sourcebatches()
+ if sourcebatches and sourcebatches["c++.build.modules"] then
+ local cachedir = path.join(dep:autogendir(), "rules", "modules", "cache")
+ target:add("cxxflags", "-fmodules-cache-path=" .. cachedir, {force = true})
+ target:add("cxxflags", "-fimplicit-modules", "-fimplicit-module-maps", "-fprebuilt-module-path=" .. cachedir, {force = true})
+ end
+ end
+end
+
+-- build module files
+function build_with_batchjobs(target, batchjobs, sourcebatch, opt)
+
+ -- get modules flag
+ local modulesflag
+ local compinst = compiler.load("cxx", {target = target})
+ if compinst:has_flags("-fmodules") then
+ modulesflag = "-fmodules"
+ elseif compinst:has_flags("-fmodules-ts") then
+ modulesflag = "-fmodules-ts"
+ end
+ assert(modulesflag, "compiler(clang): does not support c++ module!")
+
+ -- the module cache directory
+ local cachedir = path.join(target:autogendir(), "rules", "modules", "cache")
+
+ -- we need patch objectfiles to sourcebatch for linking module objects
+ local modulefiles = {}
+ sourcebatch.sourcekind = "cxx"
+ sourcebatch.objectfiles = sourcebatch.objectfiles or {}
+ sourcebatch.dependfiles = sourcebatch.dependfiles or {}
+ for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
+ local modulefile = path.join(cachedir, path.basename(sourcefile) .. ".pcm")
+ local objectfile = target:objectfile(sourcefile)
+ table.insert(sourcebatch.objectfiles, objectfile)
+ table.insert(sourcebatch.dependfiles, target:dependfile(objectfile))
+ table.insert(modulefiles, modulefile)
+ end
+
+ -- load moduledeps
+ local moduledeps = module_parser.load(target, sourcebatch, opt)
+
+ -- build moduledeps
+ local moduledeps_files = module_parser.build(moduledeps)
+
+ -- compile module files to object files
+ local count = 0
+ local sourcefiles_total = #sourcebatch.sourcefiles
+ for i = 1, sourcefiles_total do
+ local sourcefile = sourcebatch.sourcefiles[i]
+ local moduledep = assert(moduledeps_files[sourcefile], "moduledep(%s) not found!", sourcefile)
+ moduledep.job = batchjobs:newjob(sourcefile, function (index, total)
+
+ -- compile module files to *.pcm
+ local opt2 = table.join(opt, {configs = {force = {cxxflags = {modulesflag,
+ "-fimplicit-modules", "-fimplicit-module-maps", "-fprebuilt-module-path=" .. cachedir,
+ "--precompile", "-x c++-module", "-fmodules-cache-path=" .. cachedir}}}})
+ opt2.progress = (index * 100) / total
+ opt2.objectfile = modulefiles[i]
+ opt2.dependfile = target:dependfile(opt2.objectfile)
+ opt2.sourcekind = assert(sourcebatch.sourcekind, "%s: sourcekind not found!", sourcefile)
+ objectbuilder.build_object(target, sourcefile, opt2)
+
+ -- compile *.pcm to object files
+ opt2.configs = {force = {cxxflags = {modulesflag, "-fmodules-cache-path=" .. cachedir,
+ "-fimplicit-modules", "-fimplicit-module-maps", "-fprebuilt-module-path=" .. cachedir}}}
+ opt2.quiet = true
+ opt2.objectfile = sourcebatch.objectfiles[i]
+ opt2.dependfile = sourcebatch.dependfiles[i]
+ objectbuilder.build_object(target, modulefiles[i], opt2)
+
+ -- add module flags to other c++ files after building all modules
+ count = count + 1
+ if count == sourcefiles_total then
+ target:add("cxxflags", modulesflag, "-fmodules-cache-path=" .. cachedir, {force = true})
+ -- FIXME It is invalid for the module implementation unit
+ --target:add("cxxflags", "-fimplicit-modules", "-fimplicit-module-maps", "-fprebuilt-module-path=" .. cachedir, {force = true})
+ for _, modulefile in ipairs(modulefiles) do
+ target:add("cxxflags", "-fmodule-file=" .. modulefile, {force = true})
+ end
+ end
+
+ end)
+ end
+
+ -- build batchjobs
+ local rootjob = opt.rootjob
+ for _, moduledep in pairs(moduledeps) do
+ if moduledep.parents then
+ for _, parent in ipairs(moduledep.parents) do
+ batchjobs:add(moduledep.job, parent.job)
+ end
+ else
+ batchjobs:add(moduledep.job, rootjob)
+ end
+ end
+end
+
diff --git a/xmake/rules/c++/modules/build_modules/gcc.lua b/xmake/rules/c++/modules/build_modules/gcc.lua
new file mode 100644
index 000000000..2abaa4f7f
--- /dev/null
+++ b/xmake/rules/c++/modules/build_modules/gcc.lua
@@ -0,0 +1,96 @@
+--!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, TBOOX Open Source Group.
+--
+-- @author ruki
+-- @file gcc.lua
+--
+
+-- imports
+import("core.tool.compiler")
+import("private.action.build.object", {alias = "objectbuilder"})
+import("module_parser")
+
+-- load parent target with modules files
+function load_parent(target, opt)
+ -- get modules flag
+ local modulesflag
+ local compinst = compiler.load("cxx", {target = target})
+ if compinst:has_flags("-fmodules-ts") then
+ modulesflag = "-fmodules-ts"
+ end
+ assert(modulesflag, "compiler(gcc): does not support c++ module!")
+
+ -- add module flags
+ target:add("cxxflags", modulesflag)
+end
+
+-- build module files
+function build_with_batchjobs(target, batchjobs, sourcebatch, opt)
+
+ -- get modules flag
+ local modulesflag
+ local compinst = compiler.load("cxx", {target = target})
+ if compinst:has_flags("-fmodules-ts") then
+ modulesflag = "-fmodules-ts"
+ end
+ assert(modulesflag, "compiler(gcc): does not support c++ module!")
+
+ -- we need patch objectfiles to sourcebatch for linking module objects
+ sourcebatch.sourcekind = "cxx"
+ sourcebatch.objectfiles = sourcebatch.objectfiles or {}
+ sourcebatch.dependfiles = sourcebatch.dependfiles or {}
+ for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
+ local objectfile = target:objectfile(sourcefile)
+ table.insert(sourcebatch.objectfiles, objectfile)
+ table.insert(sourcebatch.dependfiles, target:dependfile(objectfile))
+ end
+
+ -- load moduledeps
+ local moduledeps = module_parser.load(target, sourcebatch, opt)
+
+ -- build moduledeps
+ local moduledeps_files = module_parser.build(moduledeps)
+
+ -- compile module files to object files
+ for i = 1, #sourcebatch.sourcefiles do
+ local sourcefile = sourcebatch.sourcefiles[i]
+ local moduledep = assert(moduledeps_files[sourcefile], "moduledep(%s) not found!", sourcefile)
+ moduledep.job = batchjobs:newjob(sourcefile, function (index, total)
+ local opt2 = table.join(opt, {configs = {force = {cxxflags = {"-x c++"}}}})
+ opt2.progress = (index * 100) / total
+ opt2.objectfile = sourcebatch.objectfiles[i]
+ opt2.dependfile = sourcebatch.dependfiles[i]
+ opt2.sourcekind = assert(sourcebatch.sourcekind, "%s: sourcekind not found!", sourcefile)
+ objectbuilder.build_object(target, sourcefile, opt2)
+ end)
+ end
+
+ -- add module flags
+ target:add("cxxflags", modulesflag)
+
+ -- build batchjobs
+ local rootjob = opt.rootjob
+ for _, moduledep in pairs(moduledeps) do
+ if moduledep.parents then
+ for _, parent in ipairs(moduledep.parents) do
+ batchjobs:add(moduledep.job, parent.job)
+ end
+ else
+ batchjobs:add(moduledep.job, rootjob)
+ end
+ end
+end
+
diff --git a/xmake/rules/c++/modules/build_modules/module_parser.lua b/xmake/rules/c++/modules/build_modules/module_parser.lua
new file mode 100644
index 000000000..b97b7e1d0
--- /dev/null
+++ b/xmake/rules/c++/modules/build_modules/module_parser.lua
@@ -0,0 +1,118 @@
+--!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, TBOOX Open Source Group.
+--
+-- @author ruki
+-- @file module_parser.lua
+--
+
+-- imports
+import("core.project.depend")
+
+-- get depend file of module source file
+function _get_dependfile_of_modulesource(target, sourcefile)
+ return target:dependfile(sourcefile)
+end
+
+-- get depend file of module object file, compiler will rewrite it
+function _get_dependfile_of_moduleobject(target, sourcefile)
+ local objectfile = target:objectfile(sourcefile)
+ return target:dependfile(objectfile)
+end
+
+-- generate module deps for the given file
+function _generate_moduledeps(target, sourcefile, opt)
+ local dependfile = _get_dependfile_of_modulesource(target, sourcefile)
+ depend.on_changed(function ()
+
+ -- trace
+ vprint("generating.moduledeps %s", sourcefile)
+
+ -- generating deps
+ local module_name
+ local module_deps
+ local sourcecode = io.readfile(sourcefile)
+ sourcecode = sourcecode:gsub("//.-\n", "\n")
+ sourcecode = sourcecode:gsub("/%*.-%*/", "")
+ for _, line in ipairs(sourcecode:split("\n", {plain = true})) do
+ if not module_name then
+ module_name = line:match("export%s+module%s+(.+)%s*;")
+ end
+ local module_depname = line:match("import%s+(.+)%s*;")
+ if module_depname then
+ -- partition? import :xxx;
+ if module_depname:startswith(":") then
+ module_depname = module_name .. module_depname
+ end
+ module_deps = module_deps or {}
+ table.insert(module_deps, module_depname)
+ end
+ end
+
+ -- save depend data
+ if module_name then
+ local dependinfo = {moduleinfo = {name = module_name, deps = module_deps, file = sourcefile}}
+ return dependinfo
+ end
+
+ end, {dependfile = dependfile, files = {sourcefile}})
+end
+
+-- generate module deps
+function generate(target, sourcebatch, opt)
+ for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
+ _generate_moduledeps(target, sourcefile, opt)
+ end
+end
+
+-- load module deps
+function load(target, sourcebatch, opt)
+
+ -- do generate first
+ generate(target, sourcebatch, opt)
+
+ -- load deps
+ local moduledeps
+ for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
+ local dependfile = _get_dependfile_of_modulesource(target, sourcefile)
+ if os.isfile(dependfile) then
+ local data = io.load(dependfile)
+ if data then
+ local moduleinfo = data.moduleinfo
+ moduledeps = moduledeps or {}
+ moduledeps[moduleinfo.name] = moduleinfo
+ end
+ end
+ end
+ return moduledeps
+end
+
+-- build module deps
+function build(moduledeps)
+ local moduledeps_files = {}
+ for _, moduledep in pairs(moduledeps) do
+ if moduledep.deps then
+ for _, depname in ipairs(moduledep.deps) do
+ local dep = moduledeps[depname]
+ if dep then
+ dep.parents = dep.parents or {}
+ table.insert(dep.parents, moduledep)
+ end
+ end
+ end
+ moduledeps_files[moduledep.file] = moduledep
+ end
+ return moduledeps_files
+end
diff --git a/xmake/rules/c++/modules/build_modules/msvc.lua b/xmake/rules/c++/modules/build_modules/msvc.lua
new file mode 100644
index 000000000..1dc04618f
--- /dev/null
+++ b/xmake/rules/c++/modules/build_modules/msvc.lua
@@ -0,0 +1,181 @@
+--!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, TBOOX Open Source Group.
+--
+-- @author ruki
+-- @file msvc.lua
+--
+
+-- imports
+import("core.tool.compiler")
+import("private.action.build.object", {alias = "objectbuilder"})
+import("module_parser")
+
+-- load parent target with modules files
+function load_parent(target, opt)
+
+ -- get modules flag
+ local modulesflag
+ local compinst = compiler.load("cxx", {target = target})
+ if compinst:has_flags("/experimental:module", "cxxflags") then
+ modulesflag = "/experimental:module"
+ end
+ assert(modulesflag, "compiler(msvc): does not support c++ module!")
+
+ -- add module flags
+ target:add("cxxflags", modulesflag)
+
+ -- get output flag
+ if compinst:has_flags("/ifcOutput", "cxxflags") then
+ for _, dep in ipairs(target:orderdeps()) do
+ local sourcebatches = dep:sourcebatches()
+ if sourcebatches and sourcebatches["c++.build.modules"] then
+ local cachedir = path.join(dep:autogendir(), "rules", "modules", "cache")
+ target:add("cxxflags", "/ifcSearchDir " .. os.args(cachedir), {force = true})
+ end
+ end
+ end
+end
+
+-- build module files
+function build_with_batchjobs(target, batchjobs, sourcebatch, opt)
+
+ -- get modules flag
+ local modulesflag
+ local compinst = compiler.load("cxx", {target = target})
+ if compinst:has_flags("/experimental:module", "cxxflags") then
+ modulesflag = "/experimental:module"
+ end
+ assert(modulesflag, "compiler(msvc): does not support c++ module!")
+
+ -- get output flag
+ local cachedir
+ local outputflag
+ if compinst:has_flags("/ifcOutput", "cxxflags") then
+ outputflag = "/ifcOutput"
+ cachedir = path.join(target:autogendir(), "rules", "modules", "cache")
+ if not os.isdir(cachedir) then
+ os.mkdir(cachedir)
+ end
+ elseif compinst:has_flags("/module:output", "cxxflags") then
+ outputflag = "/module:output"
+ end
+ assert(outputflag, "compiler(msvc): does not support c++ module!")
+
+ -- get interface flag
+ local interfaceflag
+ if compinst:has_flags("/interface", "cxxflags") then
+ interfaceflag = "/interface"
+ elseif compinst:has_flags("/module:interface", "cxxflags") then
+ interfaceflag = "/module:interface"
+ end
+ assert(interfaceflag, "compiler(msvc): does not support c++ module!")
+
+ -- get reference flag
+ local referenceflag
+ if compinst:has_flags("/reference", "cxxflags") then
+ referenceflag = "/reference"
+ elseif compinst:has_flags("/module:interface", "cxxflags") then
+ referenceflag = "/module:reference"
+ end
+ assert(referenceflag, "compiler(msvc): does not support c++ module!")
+
+ -- get stdifcdir flag
+ local stdifcdirflag
+ if compinst:has_flags("/stdIfcDir", "cxxflags") then
+ stdifcdirflag = "/stdIfcDir"
+ elseif compinst:has_flags("/module:stdIfcDir", "cxxflags") then
+ stdifcdirflag = "/module:stdIfcDir"
+ end
+ assert(stdifcdirflag, "compiler(msvc): does not support c++ module!")
+
+ -- we need patch objectfiles to sourcebatch for linking module objects
+ local modulefiles = {}
+ sourcebatch.sourcekind = "cxx"
+ sourcebatch.objectfiles = sourcebatch.objectfiles or {}
+ sourcebatch.dependfiles = sourcebatch.dependfiles or {}
+ for _, sourcefile in ipairs(sourcebatch.sourcefiles) do
+ local objectfile = target:objectfile(sourcefile)
+ local dependfile = target:dependfile(objectfile)
+ local modulefile = (cachedir and path.join(cachedir, path.basename(sourcefile)) or objectfile) .. ".ifc"
+ table.insert(modulefiles, modulefile)
+ table.insert(sourcebatch.objectfiles, objectfile)
+ table.insert(sourcebatch.dependfiles, dependfile)
+ end
+
+ -- load moduledeps
+ local moduledeps = module_parser.load(target, sourcebatch, opt)
+
+ -- build moduledeps
+ local moduledeps_files = module_parser.build(moduledeps)
+
+ -- compile module files to object files
+ local count = 0
+ local sourcefiles_total = #sourcebatch.sourcefiles
+ for i = 1, sourcefiles_total do
+ local sourcefile = sourcebatch.sourcefiles[i]
+ local moduledep = assert(moduledeps_files[sourcefile], "moduledep(%s) not found!", sourcefile)
+ moduledep.job = batchjobs:newjob(sourcefile, function (index, total)
+ local opt2 = table.join(opt, {configs = {force = {cxxflags = {interfaceflag,
+ outputflag .. " " .. os.args(modulefiles[i]), "/TP"}}}})
+ opt2.progress = (index * 100) / total
+ opt2.objectfile = sourcebatch.objectfiles[i]
+ opt2.dependfile = sourcebatch.dependfiles[i]
+ opt2.sourcekind = assert(sourcebatch.sourcekind, "%s: sourcekind not found!", sourcefile)
+ objectbuilder.build_object(target, sourcefile, opt2)
+
+ -- add module flags to other c++ files after building all modules
+ count = count + 1
+ if count == sourcefiles_total and not cachedir then
+ for _, modulefile in ipairs(modulefiles) do
+ target:add("cxxflags", referenceflag .. " " .. os.args(modulefile))
+ end
+ end
+ end)
+ end
+
+ -- add module flags
+ target:add("cxxflags", modulesflag)
+ if cachedir then
+ target:add("cxxflags", "/ifcSearchDir " .. os.args(cachedir))
+ end
+ if stdifcdirflag then
+ for _, toolchain_inst in ipairs(target:toolchains()) do
+ if toolchain_inst:name() == "msvc" then
+ local vcvars = toolchain_inst:config("vcvars")
+ if vcvars.VCInstallDir and vcvars.VCToolsVersion then
+ local stdifcdir = path.join(vcvars.VCInstallDir, "Tools", "MSVC", vcvars.VCToolsVersion, "ifc", target:is_arch("x64") and "x64" or "x86")
+ if os.isdir(stdifcdir) then
+ target:add("cxxflags", stdifcdirflag .. " " .. winos.short_path(stdifcdir))
+ end
+ end
+ break
+ end
+ end
+ end
+
+ -- build batchjobs
+ local rootjob = opt.rootjob
+ for _, moduledep in pairs(moduledeps) do
+ if moduledep.parents then
+ for _, parent in ipairs(moduledep.parents) do
+ batchjobs:add(moduledep.job, parent.job)
+ end
+ else
+ batchjobs:add(moduledep.job, rootjob)
+ end
+ end
+end
+
diff --git a/xmake/rules/c++/modules/xmake.lua b/xmake/rules/c++/modules/xmake.lua
index dfaf04897..c587a3912 100644
--- a/xmake/rules/c++/modules/xmake.lua
+++ b/xmake/rules/c++/modules/xmake.lua
@@ -21,5 +21,43 @@
-- define rule: c++.build.modules
rule("c++.build.modules")
set_extensions(".mpp", ".mxx", ".cppm", ".ixx")
- before_build_files("build_modulefiles")
+ on_config(function (target)
+ -- we disable to build across targets in parallel, because the source files may depend on other target modules
+ -- @see https://github.com/xmake-io/xmake/issues/1858
+ local target_with_modules
+ for _, dep in ipairs(target:orderdeps()) do
+ local sourcebatches = dep:sourcebatches()
+ if sourcebatches and sourcebatches["c++.build.modules"] then
+ target_with_modules = true
+ break
+ end
+ end
+ if target_with_modules then
+ -- @note this will cause cross-parallel builds to be disabled for all sub-dependent targets,
+ -- even if some sub-targets do not contain C++ modules.
+ --
+ -- maybe we will have a more fine-grained configuration strategy to disable it in the future.
+ target:set("policy", "build.across_targets_in_parallel", false)
+ if target:has_tool("cxx", "clang", "clangxx") then
+ import("build_modules.clang").load_parent(target, opt)
+ elseif target:has_tool("cxx", "gcc", "gxx") then
+ import("build_modules.gcc").load_parent(target, opt)
+ elseif target:has_tool("cxx", "cl") then
+ import("build_modules.msvc").load_parent(target, opt)
+ else
+ raise("compiler(%s): does not support c++ module!", toolname)
+ end
+ end
+ end)
+ before_build_files(function (target, batchjobs, sourcebatch, opt)
+ if target:has_tool("cxx", "clang", "clangxx") then
+ import("build_modules.clang").build_with_batchjobs(target, batchjobs, sourcebatch, opt)
+ elseif target:has_tool("cxx", "gcc", "gxx") then
+ import("build_modules.gcc").build_with_batchjobs(target, batchjobs, sourcebatch, opt)
+ elseif target:has_tool("cxx", "cl") then
+ import("build_modules.msvc").build_with_batchjobs(target, batchjobs, sourcebatch, opt)
+ else
+ raise("compiler(%s): does not support c++ module!", toolname)
+ end
+ end, {batch = true})