diff options
Diffstat (limited to 'xmake/rules/c++/modules')
| -rw-r--r-- | xmake/rules/c++/modules/builder.lua | 43 | ||||
| -rw-r--r-- | xmake/rules/c++/modules/clang/builder.lua | 8 | ||||
| -rw-r--r-- | xmake/rules/c++/modules/clang/scanner.lua | 5 | ||||
| -rw-r--r-- | xmake/rules/c++/modules/clang/support.lua | 79 | ||||
| -rw-r--r-- | xmake/rules/c++/modules/gcc/support.lua | 7 | ||||
| -rw-r--r-- | xmake/rules/c++/modules/msvc/support.lua | 7 | ||||
| -rw-r--r-- | xmake/rules/c++/modules/support.lua | 19 |
7 files changed, 140 insertions, 28 deletions
diff --git a/xmake/rules/c++/modules/builder.lua b/xmake/rules/c++/modules/builder.lua index d0863ad98..b988ff152 100644 --- a/xmake/rules/c++/modules/builder.lua +++ b/xmake/rules/c++/modules/builder.lua @@ -194,6 +194,10 @@ function should_build(target, module) local dependinfo = {} dependinfo.files = {module.sourcefile} dependinfo.values = {compinst:program(), compflags} + if module.bmifile and not module.headerunit and support.has_two_phase_compilation_support(target) then + local bmi_mode = support.has_precompile_reduced_bmi_support(target) and support.get_modulesprecompilereducedbmiflag(target) or "--precompile" + table.insert(dependinfo.values, bmi_mode) + end local objectfile_exists = (module.headerunit or support.is_bmionly(target, module.sourcefile)) and true or os.isfile(module.objectfile) dependinfo.lastmtime = (os.isfile(module.bmifile or module.objectfile) and objectfile_exists) and os.mtime(dependfile) or 0 @@ -241,6 +245,7 @@ function build_modules_for_jobgraph(target, jobgraph, built_modules) profiler.enter(target:fullname(), "c++ modules", "builder", "schedule module bmi build jobs") local builder = _builder(target) local has_two_phase_compilation_support = support.has_two_phase_compilation_support(target) + local has_precompile_reduced_bmi = support.has_precompile_reduced_bmi_support(target) local jobdeps = {} local buildfilejobs = {} @@ -272,19 +277,24 @@ function build_modules_for_jobgraph(target, jobgraph, built_modules) local buildfilejob = _get_module_buildfilejob_for(target, sourcefile, moduletype) table.insert(buildfilejobs, buildfilejob) jobgraph:add(buildfilejob, function(_, _, jobopt) - progress.set_target(jobopt.progress, target) + local moduleopt = table.clone(jobopt) + progress.set_target(moduleopt.progress, target) -- build bmi if named job - jobopt.bmi = module.name + moduleopt.bmi = module.name -- build objectfile here if two phase compilation is not supported - jobopt.objectfile = not has_two_phase_compilation_support and not bmionly - builder.make_module_job(target, module, jobopt) + moduleopt.objectfile = not has_two_phase_compilation_support and not bmionly + builder.make_module_job(target, module, moduleopt) end) _merge_jobdeps(jobdeps, _get_jobdeps(target, module, jobgraph, buildfilejob)) -- if two phase compilation supported set jobdeps for objectfile job if has_two_phase_compilation_support and not bmionly then local objbuildfilejob = _get_module_buildfilejob_for(target, sourcefile, "objectfile") - _merge_jobdeps(jobdeps, {[objbuildfilejob] = {buildfilejob}}) + if has_precompile_reduced_bmi then + _merge_jobdeps(jobdeps, _get_jobdeps(target, module, jobgraph, objbuildfilejob)) + else + _merge_jobdeps(jobdeps, {[objbuildfilejob] = {buildfilejob}}) + end end end end) @@ -327,10 +337,11 @@ function build_objectfiles_for_jobgraph(target, jobgraph, built_modules) local module = mapper.get(target, sourcefile) local buildfilejob = _get_module_buildfilejob_for(target, sourcefile, "objectfile") jobgraph:add(buildfilejob, function(_, _, jobopt) - progress.set_target(jobopt.progress, target) - jobopt.bmi = false - jobopt.objectfile = true - builder.make_module_job(target, module, jobopt) + local moduleopt = table.clone(jobopt) + progress.set_target(moduleopt.progress, target) + moduleopt.bmi = false + moduleopt.objectfile = true + builder.make_module_job(target, module, moduleopt) end) end end @@ -719,6 +730,16 @@ function build_bmis(target, jobgraph, _, opt) local built_modules, built_headerunits, _ = scanner.sort_modules_by_dependencies(target, modules, {jobgraph = target:policy("build.jobgraph")}) local headerunits, stlheaderunits = scanner.sort_headerunits(target, built_headerunits) if jobgraph.add_orders then -- jobgraph + if support.has_precompile_reduced_bmi_support(target) then + -- schedule object jobs in the BMI stage so both jobs can run concurrently + -- after their imported BMIs are ready + local append_requires_flags = _builder(target).append_requires_flags + if append_requires_flags then + append_requires_flags(target, built_modules) + end + build_objectfiles_for_jobgraph(target, jobgraph, built_modules) + end + -- build headerunits if stlheaderunits or headerunits then build_headerunits_for_jobgraph(target, jobgraph, stlheaderunits, headerunits) @@ -760,6 +781,10 @@ function build_objectfiles(target, jobgraph, _, opt) if target:is_moduleonly() and not target:data("cxx.modules.reused") or target:is_phony() then return end + if jobgraph.add_orders and support.has_precompile_reduced_bmi_support(target) then + -- object jobs have already been scheduled by build_bmis() + return + end profiler.enter(target:fullname(), "c++ modules", "builder", "objectfiles") local modules = scanner.get_modules(target) -- avoid building non referenced modules diff --git a/xmake/rules/c++/modules/clang/builder.lua b/xmake/rules/c++/modules/clang/builder.lua index 4839733e0..4ecafb17e 100644 --- a/xmake/rules/c++/modules/clang/builder.lua +++ b/xmake/rules/c++/modules/clang/builder.lua @@ -65,6 +65,8 @@ function _make_modulebuildflags(target, module, opt) local modules_reduced_bmi_flag = support.get_modulesreducedbmiflag(target) local has_two_phases = target:policy("build.c++.modules.two_phases") + local has_precompile_reduced_bmi = support.has_precompile_reduced_bmi_support(target) + local modules_precompile_reduced_bmi_flag = has_precompile_reduced_bmi and support.get_modulesprecompilereducedbmiflag(target) local flags if opt.bmi then local module_outputflag = support.get_moduleoutputflag(target) @@ -72,7 +74,7 @@ function _make_modulebuildflags(target, module, opt) flags = {"-x", "c++-module"} if not opt.objectfile then - table.insert(flags, "--precompile") + table.insert(flags, modules_precompile_reduced_bmi_flag or "--precompile") if target:has_tool("cxx", "clang_cl") then table.join2(flags, "/clang:-o", "/clang:" .. module.bmifile) end @@ -92,7 +94,7 @@ function _make_modulebuildflags(target, module, opt) end else flags = {} - if not has_two_phases or not module.bmifile then + if (has_precompile_reduced_bmi and support.has_module_extension(module.sourcefile)) or not has_two_phases or not module.bmifile then flags = {"-x", "c++"} end local std = (module.name == "std" or module.name == "std.compat") @@ -171,7 +173,7 @@ function _compile(target, flags, module, opt) opt = opt or {} local sourcefile = module.sourcefile - if not opt.bmi and opt.objectfile and module.bmifile then + if not support.has_precompile_reduced_bmi_support(target) and not opt.bmi and opt.objectfile and module.bmifile then sourcefile = module.bmifile end local outputfile = ((opt.bmi and not opt.objectfile) or opt.headerunit) and module.bmifile or module.objectfile diff --git a/xmake/rules/c++/modules/clang/scanner.lua b/xmake/rules/c++/modules/clang/scanner.lua index d73c40621..f0023cb7c 100644 --- a/xmake/rules/c++/modules/clang/scanner.lua +++ b/xmake/rules/c++/modules/clang/scanner.lua @@ -59,7 +59,8 @@ function scan_dependency_for(target, sourcefile, rescan, opt) if option.get("verbose") then print(os.args(table.join(clangscandeps, dependency_flags))) end - local outdata, errdata = os.iorunv(clangscandeps, dependency_flags) + local outdata, errdata = os.iorunv(clangscandeps, dependency_flags, + {envs = compinst:runenvs()}) assert(outdata, errdata) io.writefile(jsonfile, outdata) @@ -77,7 +78,7 @@ function scan_dependency_for(target, sourcefile, rescan, opt) end) local ifile = path.translate(path.join(outputdir, path.filename(file) .. ".i")) compflags = table.join(compflags or {}, keepsystemincludesflag or {}, {"-E", "-x", "c++", file, "-o", ifile}) - os.vrunv(compinst:program(), compflags) + os.vrunv(compinst:program(), compflags, {envs = compinst:runenvs()}) local content = io.readfile(ifile) os.rm(ifile) return content diff --git a/xmake/rules/c++/modules/clang/support.lua b/xmake/rules/c++/modules/clang/support.lua index b1699d21a..a010fdde1 100644 --- a/xmake/rules/c++/modules/clang/support.lua +++ b/xmake/rules/c++/modules/clang/support.lua @@ -46,7 +46,10 @@ function _get_toolchain_includedirs_for_stlheaders(target, includedirs, clang) table.insert(argv, 1, "-stdlib=libstdc++") end end - local result = try {function () return os.iorunv(clang, argv, {envs = compinst:runenvs()}) end} + local compinst = target:compiler("cxx") + local result = try {function () + return os.iorunv(clang, argv, {envs = compinst and compinst:runenvs()}) + end} if result then for _, line in ipairs(result:split("\n", {plain = true})) do local line = line:trim() @@ -64,17 +67,25 @@ end function _get_std_module_manifest_path(target) local print_module_manifest_flag = get_print_library_module_manifest_path_flag(target) - local clang_path = path.directory(get_clang_path(target)) + local clang_path = get_clang_path(target) + if not clang_path then + return + end + local clang_dir = path.directory(clang_path) if print_module_manifest_flag then local compinst = target:compiler("cxx") - local outdata, _ = try { function() return os.iorunv(compinst:program(), {"-std=c++23", "-stdlib=libc++", "--sysroot=" .. path.join(clang_path, ".."), print_module_manifest_flag}, {envs = compinst:runenvs()}) end } + local sysroot = "--sysroot=" .. path.join(clang_dir, "..") + local flags = {"-std=c++23", "-stdlib=libc++", sysroot, print_module_manifest_flag} + local outdata, _ = try {function() + return os.iorunv(compinst:program(), flags, {envs = compinst:runenvs()}) + end} if outdata and not outdata:startswith("<NOT PRESENT>") then return outdata:trim() end end -- fallback on custom detection -- manifest can be found in <llvm_path>/lib subdirectory (i.e on debian it should be <llvm_path>/lib/x86_64-unknown-linux-gnu/) - local clang_lib_path = path.join(clang_path, "..", "lib") + local clang_lib_path = path.join(clang_dir, "..", "lib") local modules_json_path = path.join(clang_lib_path, "libc++.modules.json") if not os.isfile(modules_json_path) then modules_json_path = find_file("*/libc++.modules.json", clang_lib_path) @@ -119,6 +130,13 @@ function has_two_phase_compilation_support(target) return target:policy("build.c++.modules.two_phases") end +function has_precompile_reduced_bmi_support(target) + if not target:policy("build.c++.modules.clang.precompile_reduced_bmi") or not has_two_phase_compilation_support(target) then + return false + end + return get_modulesprecompilereducedbmiflag(target) ~= nil +end + -- flags that doesn't affect bmi generation function strippeable_flags() -- speculative list as there is no resource that list flags that prevent reusability, this list will likely be improve over time @@ -161,7 +179,11 @@ function toolchain_includedirs(target) runtime_flag = "-stdlib=libstdc++" end end - local _, result = try {function () return os.iorunv(clang, table.join({"-E", "-Wp,-v", "-xc++", os.nuldev()}, runtime_flag or {})) end} + local compinst = target:compiler("cxx") + local flags = table.join({"-E", "-Wp,-v", "-xc++", os.nuldev()}, runtime_flag or {}) + local _, result = try {function () + return os.iorunv(clang, flags, {envs = compinst and compinst:runenvs()}) + end} if result then for _, line in ipairs(result:split("\n", {plain = true})) do local line = line:trim() @@ -183,8 +205,10 @@ function get_clang_path(target) if not clang_path then local program, toolname = target:tool("cxx") if program and toolname:startswith("clang") then + local compinst = target:compiler("cxx") + local envs = compinst and compinst:runenvs() or os.getenvs() local clang = find_tool(toolname, {program = program, - envs = os.getenvs(), cachekey = "modules_support_clang_" .. toolname}) + envs = envs, cachekey = "modules_support_clang_" .. toolname}) if clang then clang_path = clang.program end @@ -201,8 +225,10 @@ function get_clang_version(target) if not clang_version then local program, toolname = target:tool("cxx") if program and toolname:startswith("clang") then + local compinst = target:compiler("cxx") + local envs = compinst and compinst:runenvs() or os.getenvs() local clang = find_tool(toolname, {program = program, version = true, - envs = os.getenvs(), cachekey = "modules_support_clang_" .. toolname}) + envs = envs, cachekey = "modules_support_clang_" .. toolname}) if clang then clang_version = clang.version end @@ -229,10 +255,13 @@ function get_clang_scan_deps(target) if dir and dir ~= "." and os.isdir(dir) then program = path.join(dir, program) end - local result = find_tool("clang-scan-deps", {program = program, version = true}) + local compinst = target:compiler("cxx") + local envs = compinst and compinst:runenvs() or os.getenvs() + local result = find_tool("clang-scan-deps", + {program = program, version = true, envs = envs}) if not result then -- find a system wide alternative - result = find_tool("clang-scan-deps", {version = true}) + result = find_tool("clang-scan-deps", {version = true, envs = envs}) end if result then clang_scan_deps = result.program @@ -295,9 +324,16 @@ function get_stdmodules(target) return {path.normalize(path.join(try_std_module_directory, "std.cppm")), path.normalize(path.join(try_std_module_directory, "std.compat.cppm"))} end -- then try the directory relative to clang bin directory - try_std_module_directory = path.join(path.directory(get_original_file(get_clang_path(target))), std_module_directory) - if os.isdir(try_std_module_directory) then - return {path.normalize(path.join(try_std_module_directory, "std.cppm")), path.normalize(path.join(try_std_module_directory, "std.compat.cppm"))} + local clang_path = get_clang_path(target) + if clang_path then + local clang_dir = path.directory(get_original_file(clang_path)) + try_std_module_directory = path.join(clang_dir, std_module_directory) + if os.isdir(try_std_module_directory) then + return { + path.normalize(path.join(try_std_module_directory, "std.cppm")), + path.normalize(path.join(try_std_module_directory, "std.compat.cppm")) + } + end end elseif cpplib == "stdc++" then -- dont be greedy and don't enable stdc++ std module support for llvm < 19 @@ -382,6 +418,25 @@ function get_modulesreducedbmiflag(target) return modulesreducedbmiflag or nil end +function get_modulesprecompilereducedbmiflag(target) + local modulesprecompilereducedbmiflag = _g.modulesprecompilereducedbmiflag + if modulesprecompilereducedbmiflag == nil then + local compinst = target:compiler("cxx") + local checkflags = "--precompile-reduced-bmi" + if target:has_tool("cxx", "clang_cl") then + -- clang_cl.has_flags() appends -c and treats unused-argument warnings as errors, + -- but --precompile-reduced-bmi does not use -c. Ignore that specific warning and + -- treat unknown-argument warnings as errors to reject unsupported flags. + checkflags = {checkflags, "-Wno-unused-command-line-argument", "-Werror=unknown-argument"} + end + if compinst:has_flags(checkflags, "cxxflags", {flagskey = "clang_modules_precompile_reduced_bmi", tryrun = true}) then + modulesprecompilereducedbmiflag = "--precompile-reduced-bmi" + end + _g.modulesprecompilereducedbmiflag = modulesprecompilereducedbmiflag or false + end + return modulesprecompilereducedbmiflag or nil +end + function has_clangscandepssupport(target) local support_clangscandeps = _g.support_clangscandeps if support_clangscandeps == nil then diff --git a/xmake/rules/c++/modules/gcc/support.lua b/xmake/rules/c++/modules/gcc/support.lua index 1204d207d..e57bd5a63 100644 --- a/xmake/rules/c++/modules/gcc/support.lua +++ b/xmake/rules/c++/modules/gcc/support.lua @@ -79,6 +79,13 @@ function has_two_phase_compilation_support(_) return false end +function has_precompile_reduced_bmi_support(_) + return false +end + +function get_modulesprecompilereducedbmiflag(_) +end + -- flags that doesn't affect bmi generation function strippeable_flags() -- speculative list as there is no resource that list flags that prevent reusability, this list will likely be improve over time diff --git a/xmake/rules/c++/modules/msvc/support.lua b/xmake/rules/c++/modules/msvc/support.lua index 588921d0e..caaeb9cc6 100644 --- a/xmake/rules/c++/modules/msvc/support.lua +++ b/xmake/rules/c++/modules/msvc/support.lua @@ -120,6 +120,13 @@ function has_two_phase_compilation_support(_) return false end +function has_precompile_reduced_bmi_support(_) + return false +end + +function get_modulesprecompilereducedbmiflag(_) +end + -- build c++23 standard modules if needed function get_stdmodules(target, opt) opt = opt or {} diff --git a/xmake/rules/c++/modules/support.lua b/xmake/rules/c++/modules/support.lua index 126e25c61..78d74ae6c 100644 --- a/xmake/rules/c++/modules/support.lua +++ b/xmake/rules/c++/modules/support.lua @@ -92,7 +92,15 @@ function get_cpplibrary_name(target) end elseif target:is_plat("macosx", "iphoneos", "watchos", "appletvos", "applexros", "bsd", "harmony") then return "c++" - elseif target:is_plat("linux", "mingw", "cygwin", "msys", "haiku") then + elseif target:is_plat("linux", "cygwin", "msys", "haiku") then + return "stdc++" + elseif target:is_plat("mingw") then + local toolchain_inst = target:toolchain("mingw") + local is_clang = (toolchain_inst and toolchain_inst:config("clang")) or + target:has_tool("cxx", "clang", "clangxx", "clang_cl") + if is_clang then + return "c++" + end return "stdc++" elseif target:is_plat("windows") then return "msstl" @@ -103,6 +111,14 @@ function has_two_phase_compilation_support(target) return _support(target).has_two_phase_compilation_support(target) end +function has_precompile_reduced_bmi_support(target) + return _support(target).has_precompile_reduced_bmi_support(target) +end + +function get_modulesprecompilereducedbmiflag(target) + return _support(target).get_modulesprecompilereducedbmiflag(target) +end + -- strip flags not relevent for module reuse function strip_flags(target, flags, opt) @@ -429,4 +445,3 @@ function add_installfiles_for_modules(target, modules) end end end - |
