Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix stdmodule culling #5375

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions tests/projects/c++/modules/cullstdmodules/src/my_module.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module my_module;

import std;

auto my_sum(std::size_t a, std::size_t b) -> std::size_t { return a + b; }
5 changes: 5 additions & 0 deletions tests/projects/c++/modules/cullstdmodules/src/my_module.mpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export module my_module;

import std;

export auto my_sum(std::size_t a, std::size_t b) -> std::size_t;
72 changes: 72 additions & 0 deletions tests/projects/c++/modules/cullstdmodules/test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import("lib.detect.find_tool")
import("core.base.semver")
import("core.tool.toolchain")
import("utils.ci.is_running", {alias = "ci_is_running"})

function _build()
local outdata
if ci_is_running() then
local outdata = os.iorun("xmake -rvD")
else
local outdata = os.iorun("xmake -r")
end
if outdata then
if outdata:find("compiling.module.release std") then
raise("STD Module culling does not work\n%s", outdata)
else
print(outdata)
end
end
outdata = os.iorun("xmake")
if outdata then
if outdata:find("compiling") or outdata:find("linking") or outdata:find("generating") then
raise("Modules incremental compilation does not work\n%s", outdata)
end
end
end

function main(t)
if is_subhost("windows") then
local clang = find_tool("clang", {version = true})
if clang and clang.version and semver.compare(clang.version, "19.0") >= 0 then
-- clang don't support msstl std modules atm
-- os.exec("xmake f --toolchain=clang -c --yes")
-- _build()
os.exec("xmake clean -a")
os.exec("xmake f --toolchain=clang --runtimes=c++_shared -c --yes")
_build()
end
local msvc = toolchain.load("msvc")
if msvc and msvc:check() then
local vcvars = msvc:config("vcvars")
if vcvars and vcvars.VCInstallDir and vcvars.VCToolsVersion and semver.compare(vcvars.VCToolsVersion, "14.35") then
local stdmodulesdir = path.join(vcvars.VCInstallDir, "Tools", "MSVC", vcvars.VCToolsVersion, "modules")
if os.isdir(stdmodulesdir) then
os.exec("xmake clean -a")
os.exec("xmake f -c --yes")
_build()
end
end
end
elseif is_subhost("msys") then
-- os.exec("xmake f -c -p mingw --yes")
-- _build()
elseif is_host("linux") then -- or is_host("macosx") then
-- gcc don't support std modules atm
-- local gcc = find_tool("gcc", {version = true})
-- if is_host("linux") and gcc and gcc.version and semver.compare(gcc.version, "11.0") >= 0 then
-- os.exec("xmake f -c --yes")
-- _build()
-- end
local clang = find_tool("clang", {version = true})
if clang and clang.version and semver.compare(clang.version, "19.0") >= 0 then
-- clang don't support libstdc++ std modules atm
-- os.exec("xmake clean -a")
-- os.exec("xmake f --toolchain=clang -c --yes")
-- _build()
os.exec("xmake clean -a")
os.exec("xmake f --toolchain=clang --runtimes=c++_shared -c --yes")
_build()
end
end
end
9 changes: 9 additions & 0 deletions tests/projects/c++/modules/cullstdmodules/test/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import std;

import my_module;

using namespace std;

int main(int argc, char** argv) {
cout << my_sum(1, 1) << endl;
}
12 changes: 12 additions & 0 deletions tests/projects/c++/modules/cullstdmodules/xmake.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
add_rules("mode.debug", "mode.release")
set_languages("c++latest")

target("mod")
set_kind("static")
add_files("src/*.cpp")
add_files("src/*.mpp", {public = true})

target("stdmodules")
set_kind("binary")
add_files("test/*.cpp")
add_deps("mod")
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ module my_module;

import std;

auto my_sum(size_t a, size_t b) -> size_t { return a + b; }
auto my_sum(std::size_t a, std::size_t b) -> std::size_t { return a + b; }
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ export module my_module;

import std;

export auto my_sum(size_t a, size_t b) -> size_t;
export auto my_sum(std::size_t a, std::size_t b) -> std::size_t;
3 changes: 2 additions & 1 deletion xmake/rules/c++/modules/modules_support/compiler_support.lua
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ function cull_objectfiles(target, modules, sourcebatch)
local public = fileconfig and fileconfig.public
local external = fileconfig and fileconfig.external
local private_dep = fileconfig and fileconfig.private_dep
if (not public and not external) or (external and private_dep) then
local cullobjectfile = fileconfig and fileconfig.cullobjectfile
if not cullobjectfile and ((not public and not external) or (external and private_dep)) then
table.insert(sourcebatch.objectfiles, objectfile)
end
else
Expand Down
67 changes: 46 additions & 21 deletions xmake/rules/c++/modules/modules_support/dependency_scanner.lua
Original file line number Diff line number Diff line change
Expand Up @@ -174,22 +174,41 @@ end

-- generate edges for DAG
function _get_edges(nodes, modules)
local edges = {}
for _, node in ipairs(nodes) do
local module = modules[node]
if module.requires then
for required_name, _ in table.orderpairs(module.requires) do
for _, required_node in ipairs(nodes) do
local name, _, _ = compiler_support.get_provided_module(modules[required_node])
if name and name == required_name then
table.insert(edges, {required_node, node})
break
end
end
end
end
end
return edges
local edges = {}
local require_std_compat_module = false
for _, node in ipairs(nodes) do
local module = modules[node]
local module_name, _, _ = compiler_support.get_provided_module(module)
if module.requires then
for required_name, _ in table.orderpairs(module.requires) do
if required_name == "std.compat" then
require_std_compat_module = true
break
end
end
end
end
for _, node in ipairs(nodes) do
local module = modules[node]
local module_name, _, cppfile = compiler_support.get_provided_module(module)
if module.requires then
for required_name, _ in table.orderpairs(module.requires) do
for _, required_node in ipairs(nodes) do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any way to optimize this many loops? it seems like it would be very slow when the object file is very large.

Or we can build the map to optimize it the first time we traverse the nodes.

local name, _, _ = compiler_support.get_provided_module(modules[required_node])
if name and name == required_name then
if module_name == "std.compat" and require_std_compat_module or (module_name ~= "std.compat") then
if module_name == "std.compat" then
target:fileconfig_set(cppfile, {cullobjectfile = false})
end
table.insert(edges, {required_node, node})
break
end
end
end
end
end
end
return edges
end

function _get_package_modules(target, package, opt)
Expand Down Expand Up @@ -422,19 +441,25 @@ function sort_modules_by_dependencies(target, objectfiles, modules)
local objectfiles_sorted_set = hashset.from(objectfiles_sorted)
for _, objectfile in ipairs(objectfiles) do
if not objectfiles_sorted_set:has(objectfile) then
local cullobjectfile = fileconfig and fileconfig.cullobjectfile
if target:policy("build.c++.modules.culling") then
-- cull unreferenced non-public named module but add non-module files and implementation modules
local _, provide, cppfile = compiler_support.get_provided_module(modules[objectfile])
local name, provide, cppfile = compiler_support.get_provided_module(modules[objectfile])
local fileconfig = target:fileconfig(cppfile)
local public = fileconfig and fileconfig.public
local dont_cull = fileconfig and fileconfig.cull ~= nil and not fileconfig.cull
if not provide or public or dont_cull then
local dont_cull = (fileconfig and fileconfig.cull ~= nil) and not fileconfig.cull
if not cullobjectfile and (not provide or public or dont_cull) then
table.insert(result, objectfile)
else
wprint("%s has been culled because it's not consumed by its target nor flagged as a public module (add_files(\"xxx.cppm\", {public = true}))", cppfile)
target:fileconfig_set(cppfile, {cullobjectfile = true})
if not (name and (name == "std" or name == "std.compat")) then
wprint("%s has been culled because it's not consumed by its target nor flagged as a public module (add_files(\"xxx.cppm\", {public = true}))", cppfile)
end
end
else
table.insert(result, objectfile)
if not cullobjectfile then
table.insert(result, objectfile)
end
end
end
end
Expand Down
6 changes: 2 additions & 4 deletions xmake/rules/c++/modules/xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,7 @@ rule("c++.build.modules.builder")
local std_modules = compiler_support.get_stdmodules(target)
if std_modules then
table.join2(sourcebatch.sourcefiles, std_modules)
target:fileconfig_set(std_modules[1], {external = true})
target:fileconfig_set(std_modules[2], {external = true})
target:fileconfig_set(std_modules[2], {cullobjectfile = true})
end

-- extract packages modules dependencies
Expand Down Expand Up @@ -150,8 +149,7 @@ rule("c++.build.modules.builder")
local std_modules = compiler_support.get_stdmodules(target)
if std_modules then
table.join2(sourcebatch.sourcefiles, std_modules)
target:fileconfig_set(std_modules[1], {external = true})
target:fileconfig_set(std_modules[2], {external = true})
target:fileconfig_set(std_modules[2], {cullobjectfile = true})
end

-- extract packages modules dependencies
Expand Down
Loading