diff options
| 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 |
| commit | ed0c0c5e0249aa966ea7061b30710abdd0b09d87 (patch) | |
| tree | 2a51c869a3aea1a093ed569e749bdf0465634a6c /xmake/rules | |
| parent | 885d00da8caf74aeed758d030b9a1b50d9078e1f (diff) | |
| parent | 2431dd7e6142ff98b55741cfd4de4d2e69f4f8b5 (diff) | |
Merged from upstream/master
Diffstat (limited to 'xmake/rules')
71 files changed, 2241 insertions, 321 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}) diff --git a/xmake/rules/c++/openmp/load.lua b/xmake/rules/c++/openmp/load.lua index a4535f553..3f0508512 100644 --- a/xmake/rules/c++/openmp/load.lua +++ b/xmake/rules/c++/openmp/load.lua @@ -20,6 +20,7 @@ -- main entry function main(target, sourcekind) + wprint("we no longer need add_rules(\"%s.openmp\") now, you just need to add add_packages(\"openmp\").", sourcekind == "cxx" and "c++" or "c") local _, compiler_name = target:tool(sourcekind) local flag_name = sourcekind == "cxx" and "cxxflags" or "cflags" if compiler_name == "cl" then diff --git a/xmake/rules/c++/openmp/xmake.lua b/xmake/rules/c++/openmp/xmake.lua index 47dd2d07f..82e3cc562 100644 --- a/xmake/rules/c++/openmp/xmake.lua +++ b/xmake/rules/c++/openmp/xmake.lua @@ -20,12 +20,12 @@ -- define rule: c.openmp rule("c.openmp") - on_load(function (target) + on_config(function (target) import("load")(target, "cc") end) -- define rule: c++.openmp rule("c++.openmp") - on_load(function (target) + on_config(function (target) import("load")(target, "cxx") end) diff --git a/xmake/rules/c++/unity_build/unity_build.lua b/xmake/rules/c++/unity_build/unity_build.lua new file mode 100644 index 000000000..608f79fed --- /dev/null +++ b/xmake/rules/c++/unity_build/unity_build.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 unity_build.lua +-- + +-- imports +import("core.project.depend") + +function _merge_unityfile(target, sourcefile_unity, sourcefiles, opt) + local dependfile = target:dependfile(sourcefile_unity) + depend.on_changed(function () + + -- trace + vprint("generating.unityfile %s", sourcefile_unity) + + -- do merge + local uniqueid = target:data("unity_build.uniqueid") + local unityfile = io.open(sourcefile_unity, "w") + for _, sourcefile in ipairs(sourcefiles) do + sourcefile = path.absolute(sourcefile) + sourcefile_unity = path.absolute(sourcefile_unity) + sourcefile = path.relative(sourcefile, path.directory(sourcefile_unity)) + if uniqueid then + unityfile:print("#define %s %s", uniqueid, "unity_" .. hash.uuid():split("-", {plain = true})[1]) + end + unityfile:print("#include \"%s\"", sourcefile) + if uniqueid then + unityfile:print("#undef %s", uniqueid) + end + end + unityfile:close() + + end, {dependfile = dependfile, files = sourcefiles}) +end + +function generate_unityfiles(target, sourcebatch, opt) + local unity_batch = target:data("unity_build.unity_batch." .. sourcebatch.rulename) + if unity_batch then + for _, sourcefile_unity in ipairs(sourcebatch.sourcefiles) do + local sourceinfo = unity_batch[sourcefile_unity] + if sourceinfo then + local sourcefiles = sourceinfo.sourcefiles + if sourcefiles then + _merge_unityfile(target, sourcefile_unity, sourcefiles, opt) + end + end + end + end +end + +-- use unity build +-- +-- e.g. +-- add_rules("c++.unity_build", {batchsize = 2}) +-- add_files("src/*.c", "src/*.cpp", {unity_ignored = true}) +-- add_files("src/foo/*.c", {unity_group = "foo"}) +-- add_files("src/bar/*.c", {unity_group = "bar"}) +-- +function main(target, sourcebatch) + + -- get unit batch sources + local extraconf = target:extraconf("rules", sourcebatch.sourcekind == "cxx" and "c++.unity_build" or "c.unity_build") + local batchsize = extraconf and extraconf.batchsize + local uniqueid = extraconf and extraconf.uniqueid + local id = 1 + local count = 0 + local unity_batch = {} + local sourcefiles = {} + local objectfiles = {} + local dependfiles = {} + local sourcedir = path.join(target:autogendir({root = true}), "unity_build") + for idx, sourcefile in pairs(sourcebatch.sourcefiles) do + local sourcefile_unity + local objectfile = sourcebatch.objectfiles[idx] + local dependfile = sourcebatch.dependfiles[idx] + local fileconfig = target:fileconfig(sourcefile) + if fileconfig and fileconfig.unity_group then + sourcefile_unity = path.join(sourcedir, "unity_" .. fileconfig.unity_group .. path.extension(sourcefile)) + elseif (fileconfig and fileconfig.unity_ignored) or (batchsize and batchsize <= 1) then + -- we do not add these files to unity file + table.insert(sourcefiles, sourcefile) + table.insert(objectfiles, objectfile) + table.insert(dependfiles, dependfile) + else + if batchsize and count > batchsize then + id = id + 1 + end + sourcefile_unity = path.join(sourcedir, "unity_" .. hash.uuid(tostring(id)):split("-", {plain = true})[1] .. path.extension(sourcefile)) + count = count + 1 + end + if sourcefile_unity then + local sourceinfo = unity_batch[sourcefile_unity] + if not sourceinfo then + sourceinfo = {} + sourceinfo.objectfile = target:objectfile(sourcefile_unity) + sourceinfo.dependfile = target:dependfile(sourceinfo.objectfile) + unity_batch[sourcefile_unity] = sourceinfo + end + sourceinfo.sourcefiles = sourceinfo.sourcefiles or {} + table.insert(sourceinfo.sourcefiles, sourcefile) + end + end + + -- use unit batch + for _, sourcefile_unity in ipairs(table.orderkeys(unity_batch)) do + local sourceinfo = unity_batch[sourcefile_unity] + if #sourceinfo.sourcefiles > 1 then + table.insert(sourcefiles, sourcefile_unity) + table.insert(objectfiles, sourceinfo.objectfile) + table.insert(dependfiles, sourceinfo.dependfile) + else + table.insert(sourcefiles, sourceinfo.sourcefiles[1]) + table.insert(objectfiles, sourceinfo.objectfile) + table.insert(dependfiles, sourceinfo.dependfile) + end + end + sourcebatch.sourcefiles = sourcefiles + sourcebatch.objectfiles = objectfiles + sourcebatch.dependfiles = dependfiles + + -- save unit batch + target:data_set("unity_build.uniqueid", uniqueid) + target:data_set("unity_build.unity_batch." .. sourcebatch.rulename, unity_batch) +end diff --git a/xmake/rules/c++/unity_build/xmake.lua b/xmake/rules/c++/unity_build/xmake.lua new file mode 100644 index 000000000..e0dfa9660 --- /dev/null +++ b/xmake/rules/c++/unity_build/xmake.lua @@ -0,0 +1,63 @@ +--!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 xmake.lua +-- + +rule("c.unity_build") + after_load(function (target) + import("unity_build") + local sourcebatches = target:sourcebatches() + if sourcebatches then + local sourcebatch = sourcebatches["c.build"] + if sourcebatch then + unity_build(target, sourcebatch) + end + end + end) + before_build(function (target, opt) + import("unity_build") + local sourcebatches = target:sourcebatches() + if sourcebatches then + local sourcebatch = sourcebatches["c.build"] + if sourcebatch then + unity_build.generate_unityfiles(target, sourcebatch, opt) + end + end + end) + +rule("c++.unity_build") + after_load(function (target) + import("unity_build") + local sourcebatches = target:sourcebatches() + if sourcebatches then + local sourcebatch = sourcebatches["c++.build"] + if sourcebatch then + unity_build(target, sourcebatch) + end + end + end) + before_build(function (target, opt) + import("unity_build") + local sourcebatches = target:sourcebatches() + if sourcebatches then + local sourcebatch = sourcebatches["c++.build"] + if sourcebatch then + unity_build.generate_unityfiles(target, sourcebatch, opt) + end + end + end) diff --git a/xmake/rules/cuda/devlink/xmake.lua b/xmake/rules/cuda/devlink/xmake.lua index e92a0b779..fb1f603b0 100644 --- a/xmake/rules/cuda/devlink/xmake.lua +++ b/xmake/rules/cuda/devlink/xmake.lua @@ -34,7 +34,7 @@ rule("cuda.build.devlink") import("core.project.depend") import("core.tool.linker") import("core.platform.platform") - import("private.utils.progress") + import("utils.progress") -- disable devlink? if target:values("cuda.build.devlink") == false then diff --git a/xmake/rules/cuda/env/xmake.lua b/xmake/rules/cuda/env/xmake.lua index e6e802d71..96d587e41 100644 --- a/xmake/rules/cuda/env/xmake.lua +++ b/xmake/rules/cuda/env/xmake.lua @@ -21,7 +21,7 @@ -- define rule: environment rule("cuda.env") - before_load(function (target) + on_load(function (target) -- imports import("detect.sdks.find_cuda") diff --git a/xmake/rules/cuda/gencodes/xmake.lua b/xmake/rules/cuda/gencodes/xmake.lua index 0845178fe..c7ee45050 100644 --- a/xmake/rules/cuda/gencodes/xmake.lua +++ b/xmake/rules/cuda/gencodes/xmake.lua @@ -34,7 +34,7 @@ rule("cuda.gencodes") -- if no available device is found, no `-gencode` flags will be added -- @seealso xmake/modules/lib/detect/find_cudadevices -- - before_load(function (target) + on_config(function (target) -- imports import("core.platform.platform") @@ -47,14 +47,14 @@ rule("cuda.gencodes") local known_r_archs = hashset.of(20, 30, 32, 35, 37, 50, 52, 53, 60, 61, 62, 70, 72, 75, 80) local function nf_cugencode(archs) - if type(archs) ~= 'string' then + if type(archs) ~= "string" then return nil end archs = archs:trim():lower() - if archs == 'native' then + if archs == "native" then local device = find_cudadevices({ skip_compute_mode_prohibited = true, order_by_flops = true })[1] if device then - return nf_cugencode('sm_' .. device.major .. device.minor) + return nf_cugencode("sm_" .. device.major .. device.minor) end return nil end @@ -68,13 +68,13 @@ rule("cuda.gencodes") end local arch = tonumber(value:sub(#prefix + 1)) or tonumber(value:sub(#prefix + 2)) if arch == nil then - raise("Unknown architecture: " .. value) + raise("unknown architecture: " .. value) end if not know_list:has(arch) then if arch <= table.maxn(know_list:data()) then - raise("Unknown architecture: " .. prefix .. "_" .. arch) + raise("unknown architecture: " .. prefix .. "_" .. arch) else - utils.warning("Unknown architecture: " .. prefix .. "_" .. arch) + utils.warning("unknown architecture: " .. prefix .. "_" .. arch) end end return arch @@ -82,20 +82,20 @@ rule("cuda.gencodes") for _, v in ipairs(archs:split(',')) do local arch = v:trim() - local temp_r_arch = parse_arch(arch, 'sm', known_r_archs) + local temp_r_arch = parse_arch(arch, "sm", known_r_archs) if temp_r_arch then table.insert(r_archs, temp_r_arch) end - local temp_v_arch = parse_arch(arch, 'compute', known_v_archs) + local temp_v_arch = parse_arch(arch, "compute", known_v_archs) if temp_v_arch then if v_arch ~= nil then - raise("More than one virtual architecture is defined in one gpu gencode option: compute_" .. v_arch .. " and compute_" .. temp_v_arch) + raise("more than one virtual architecture is defined in one gpu gencode option: compute_" .. v_arch .. " and compute_" .. temp_v_arch) end v_arch = temp_v_arch end if not (temp_r_arch or temp_v_arch) then - raise("Unknown architecture: " .. arch) + raise("unknown architecture: " .. arch) end end @@ -105,27 +105,28 @@ rule("cuda.gencodes") if #r_archs == 0 then return { - clang = '--cuda-gpu-arch=sm_' .. v_arch - , nvcc = '-gencode arch=compute_' .. v_arch .. ',code=compute_' .. v_arch } + clang = "--cuda-gpu-arch=sm_" .. v_arch, + nvcc = "-gencode arch=compute_" .. v_arch .. ",code=compute_" .. v_arch + } end if v_arch then table.insert(r_archs, v_arch) else - v_arch = math.min(unpack(r_archs)) + v_arch = math.min(table.unpack(r_archs)) end r_archs = table.unique(r_archs) local clang_flags = {} for _, r_arch in ipairs(r_archs) do - table.insert(clang_flags, '--cuda-gpu-arch=sm_' .. r_arch) + table.insert(clang_flags, "--cuda-gpu-arch=sm_" .. r_arch) end local nvcc_flags = nil if #r_archs == 1 then - nvcc_flags = '-gencode arch=compute_' .. v_arch .. ',code=sm_' .. r_archs[1] + nvcc_flags = "-gencode arch=compute_" .. v_arch .. ",code=sm_" .. r_archs[1] else - nvcc_flags = '-gencode arch=compute_' .. v_arch .. ',code=[sm_' .. table.concat(r_archs, ',sm_') .. ']' + nvcc_flags = "-gencode arch=compute_" .. v_arch .. ",code=[sm_" .. table.concat(r_archs, ",sm_") .. "]" end return { clang = clang_flags, nvcc = nvcc_flags } @@ -138,13 +139,12 @@ rule("cuda.gencodes") for _, v in ipairs(cugencodes) do local flag = nf_cugencode(v) if flag then - local tool, toolname = platform.tool("cu") - if (toolname or path.basename(tool)) == "nvcc" then - target:add('cuflags', flag.nvcc) + if target:has_tool("cu", "nvcc") then + target:add("cuflags", flag.nvcc) else - target:add('cuflags', flag.clang) + target:add("cuflags", flag.clang) end - target:add('culdflags', flag.nvcc) + target:add("culdflags", flag.nvcc) end end end) diff --git a/xmake/rules/go/build/object.lua b/xmake/rules/go/build/object.lua index cf6a1c9ff..21436be47 100644 --- a/xmake/rules/go/build/object.lua +++ b/xmake/rules/go/build/object.lua @@ -21,19 +21,13 @@ -- imports import("core.base.option") import("core.base.hashset") -import("core.theme.theme") import("core.tool.compiler") import("core.project.depend") +import("utils.progress") -- build the source files function main(target, sourcebatch, opt) - - -- is verbose? - local verbose = option.get("verbose") - - -- get progress range - local progress = assert(opt.progress, "no progress!") - + -- get source files and kind local sourcefiles = sourcebatch.sourcefiles local sourcekind = sourcebatch.sourcekind @@ -72,18 +66,11 @@ function main(target, sourcebatch, opt) -- trace progress info for index, sourcefile in ipairs(sourcefiles) do - local progress_prefix = "${color.build.progress}" .. theme.get("text.build.progress_format") .. ":${clear} " - if verbose then - cprint(progress_prefix .. "${dim color.build.object}compiling.$(mode) %s", progress, sourcefile) - else - cprint(progress_prefix .. "${color.build.object}compiling.$(mode) %s", progress, sourcefile) - end + progress.show(opt.progress, "${color.build.object}compiling.$(mode) %s", sourcefile) end -- trace verbose info - if verbose then - print(compinst:compcmd(sourcefiles, objectfile, {compflags = compflags})) - end + vprint(compinst:compcmd(sourcefiles, objectfile, {compflags = compflags})) -- compile it dependinfo.files = {} diff --git a/xmake/rules/go/xmake.lua b/xmake/rules/go/xmake.lua index ef6f04321..58890e260 100644 --- a/xmake/rules/go/xmake.lua +++ b/xmake/rules/go/xmake.lua @@ -24,6 +24,10 @@ rule("go.build") on_load(function (target) -- we disable to build across targets in parallel, because the source files may depend on other target modules target:set("policy", "build.across_targets_in_parallel", false) + -- xxx.a + if target:is_static() then + target:set("prefixname", "") + end end) on_build_files("build.object") diff --git a/xmake/rules/luarocks/module/xmake.lua b/xmake/rules/luarocks/module/xmake.lua index e74080912..530e35070 100644 --- a/xmake/rules/luarocks/module/xmake.lua +++ b/xmake/rules/luarocks/module/xmake.lua @@ -19,7 +19,7 @@ -- rule("luarocks.module") - before_load(function (target) + on_load(function (target) -- imports import("core.cache.detectcache") diff --git a/xmake/rules/mdk/xmake.lua b/xmake/rules/mdk/xmake.lua new file mode 100644 index 000000000..0c491b528 --- /dev/null +++ b/xmake/rules/mdk/xmake.lua @@ -0,0 +1,43 @@ +--!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 xmake.lua +-- + +rule("mdk.console") + on_load(function (target) + -- we disable checking flags for cross toolchain automatically + target:set("policy", "check.auto_ignore_flags", false) + target:set("policy", "check.auto_map_flags", false) + + -- set default output binary + target:set("kind", "binary") + if not target:get("extension") then + target:set("extension", ".axf") + end + end) + +rule("mdk.static") + on_load(function (target) + -- we disable checking flags for cross toolchain automatically + target:set("policy", "check.auto_ignore_flags", false) + target:set("policy", "check.auto_map_flags", false) + + -- set default output binary + target:set("kind", "static") + end) + diff --git a/xmake/rules/nim/build/target.lua b/xmake/rules/nim/build/target.lua new file mode 100644 index 000000000..79e271063 --- /dev/null +++ b/xmake/rules/nim/build/target.lua @@ -0,0 +1,86 @@ +--!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 target.lua +-- + +-- imports +import("core.base.option") +import("core.base.hashset") +import("core.tool.compiler") +import("core.project.depend") +import("utils.progress") + +-- build the source files +function build_sourcefiles(target, sourcebatch, opt) + + -- get the target file + local targetfile = target:targetfile() + + -- get source files and kind + local sourcefiles = sourcebatch.sourcefiles + local sourcekind = sourcebatch.sourcekind + + -- get depend file + local dependfile = target:dependfile(targetfile) + + -- load compiler + local compinst = compiler.load(sourcekind, {target = target}) + + -- get compile flags + local compflags = compinst:compflags({target = target}) + + -- load dependent info + local dependinfo = option.get("rebuild") and {} or (depend.load(dependfile) or {}) + + -- need build this object? + local depvalues = {compinst:program(), compflags} + if not depend.is_changed(dependinfo, {lastmtime = os.mtime(targetfile), values = depvalues}) then + return + end + + -- trace progress into + progress.show(opt.progress, "${color.build.target}linking.$(mode) %s", path.filename(targetfile)) + + -- trace verbose info + vprint(compinst:buildcmd(sourcefiles, targetfile, {target = target, compflags = compflags})) + + -- flush io buffer to update progress info + io.flush() + + -- compile it + dependinfo.files = {} + assert(compinst:build(sourcefiles, targetfile, {target = target, dependinfo = dependinfo, compflags = compflags})) + + -- update files and values to the dependent file + dependinfo.values = depvalues + table.join2(dependinfo.files, sourcefiles) + depend.save(dependinfo, dependfile) +end + +-- build target +function main(target, opt) + + -- @note only support one source kind! + local sourcebatches = target:sourcebatches() + if sourcebatches then + local sourcebatch = sourcebatches["nim.build"] + if sourcebatch then + build_sourcefiles(target, sourcebatch, opt) + end + end +end diff --git a/xmake/rules/nim/xmake.lua b/xmake/rules/nim/xmake.lua new file mode 100644 index 000000000..7d65c9c12 --- /dev/null +++ b/xmake/rules/nim/xmake.lua @@ -0,0 +1,37 @@ +--!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 xmake.lua +-- + +-- define rule: nim.build +rule("nim.build") + set_sourcekinds("nc") + on_load(function (target) + local cachedir = path.join(target:autogendir(), "nimcache") + target:add("ncflags", "--nimcache:" .. cachedir, {force = true}) + end) + on_build("build.target") + +-- define rule: nim +rule("nim") + + -- add build rules + add_deps("nim.build") + + -- inherit links and linkdirs of all dependent targets by default + add_deps("utils.inherit.links") diff --git a/xmake/rules/pascal/build/target.lua b/xmake/rules/pascal/build/target.lua new file mode 100644 index 000000000..646474606 --- /dev/null +++ b/xmake/rules/pascal/build/target.lua @@ -0,0 +1,88 @@ +--!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 target.lua +-- + +-- imports +import("core.base.option") +import("core.base.hashset") +import("core.theme.theme") +import("core.tool.compiler") +import("core.project.depend") +import("utils.progress") + +-- build the source files +function build_sourcefiles(target, sourcebatch, opt) + + -- is verbose? + local verbose = option.get("verbose") + + -- get the target file + local targetfile = target:targetfile() + + -- get source files and kind + local sourcefiles = sourcebatch.sourcefiles + local sourcekind = sourcebatch.sourcekind + + -- get depend file + local dependfile = target:dependfile(targetfile) + + -- load compiler + local compinst = compiler.load(sourcekind, {target = target}) + + -- get compile flags + local compflags = compinst:compflags({target = target}) + + -- load dependent info + local dependinfo = option.get("rebuild") and {} or (depend.load(dependfile) or {}) + + -- need build this object? + local depvalues = {compinst:program(), compflags} + if not depend.is_changed(dependinfo, {lastmtime = os.mtime(targetfile), values = depvalues}) then + return + end + + -- trace progress into + progress.show(opt.progress, "${color.build.target}linking.$(mode) %s", path.filename(targetfile)) + + -- trace verbose info + if verbose then + print(compinst:buildcmd(sourcefiles, targetfile, {target = target, compflags = compflags})) + end + + -- compile it + dependinfo.files = {} + assert(compinst:build(sourcefiles, targetfile, {target = target, dependinfo = dependinfo, compflags = compflags})) + + -- update files and values to the dependent file + dependinfo.values = depvalues + table.join2(dependinfo.files, sourcefiles) + depend.save(dependinfo, dependfile) +end + +-- build target +function main(target, opt) + + -- @note only support one source kind! + for _, sourcebatch in pairs(target:sourcebatches()) do + if sourcebatch.sourcekind == "pc" then + build_sourcefiles(target, sourcebatch, opt) + break + end + end +end diff --git a/xmake/rules/pascal/xmake.lua b/xmake/rules/pascal/xmake.lua new file mode 100644 index 000000000..5961c08d3 --- /dev/null +++ b/xmake/rules/pascal/xmake.lua @@ -0,0 +1,33 @@ +--!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 xmake.lua +-- + +-- define rule: pascal.build +rule("pascal.build") + set_sourcekinds("pc") + on_build("build.target") + +-- define rule: pascal +rule("pascal") + + -- add build rules + add_deps("pascal.build") + + -- inherit links and linkdirs of all dependent targets by default + add_deps("utils.inherit.links") diff --git a/xmake/rules/platform/linux/driver/driver_modules.lua b/xmake/rules/platform/linux/driver/driver_modules.lua new file mode 100644 index 000000000..4f2640c83 --- /dev/null +++ b/xmake/rules/platform/linux/driver/driver_modules.lua @@ -0,0 +1,299 @@ +--!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 driver_modules.lua +-- + +-- imports +import("core.base.option") +import("core.project.depend") +import("core.cache.memcache") +import("lib.detect.find_tool") +import("utils.progress") +import("private.tools.ccache") + +-- get linux-headers sdk +function _get_linux_headers_sdk(target) + local linux_headersdir = target:values("linux.driver.linux-headers") + if linux_headersdir then + return {sdkdir = linux_headersdir, includedir = path.join(linux_headersdir, "include")} + end + local linux_headers = assert(target:pkg("linux-headers"), "please add `add_requires(\"linux-headers\", {configs = {driver_modules = true}})` and `add_packages(\"linux-headers\")` to the given target!") + local includedirs = linux_headers:get("includedirs") or linux_headers:get("sysincludedirs") + local version = linux_headers:version() + local includedir + for _, dir in ipairs(includedirs) do + if dir:find("linux-headers", 1, true) then + includedir = dir + linux_headersdir = path.directory(dir) + break + end + end + assert(linux_headersdir, "linux-headers not found!") + if not os.isfile(path.join(includedir, "generated/autoconf.h")) and + not os.isfile(path.join(includedir, "config/auto.conf")) then + raise("kernel configuration is invalid. include/generated/autoconf.h or include/config/auto.conf are missing.") + end + return {version = version, sdkdir = linux_headersdir, includedir = includedir} +end + +-- get cflags from make +function _get_cflags_from_make(target, sdkdir) + local key = target:plat() .. target:arch() + local cflags = memcache.get2("linux.driver", key, "cflags") + local ldflags_o = memcache.get2("linux.driver", key, "ldflags_o") + local ldflags_ko = memcache.get2("linux.driver", key, "ldflags_ko") + if cflags == nil then + local make = assert(find_tool("make"), "make not found!") + local tmpdir = os.tmpfile() .. ".dir" + local makefile = path.join(tmpdir, "Makefile") + local stubfile = path.join(tmpdir, "src/stub.c") + local foofile = path.join(tmpdir, "src/foo.c") + io.writefile(makefile, [[obj-m := stub.o +stub-objs := src/stub.o src/foo.o]]) + io.writefile(foofile, "") + io.writefile(stubfile, [[ +#include <linux/init.h> +#include <linux/module.h> + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Ruki"); +MODULE_DESCRIPTION("A simple Hello World Module"); +MODULE_ALIAS("a simplest module"); + +int hello_init(void) { + printk(KERN_INFO "Hello World\n"); + return 0; +} + +void hello_exit(void) { + printk(KERN_INFO "Goodbye World\n"); +} + +module_init(hello_init); +module_exit(hello_exit); + ]]) + local argv = {"-C", sdkdir, "V=1", "M=" .. tmpdir, "modules"} + if not target:is_plat(os.subhost()) then + -- e.g. $(MAKE) -C $(KERN_DIR) V=1 ARCH=arm64 CROSS_COMPILE=/mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu- M=$(PWD) modules + local arch + if target:is_arch("arm", "armv7") then + arch = "arm" + elseif target:is_arch("arm64", "arm64-v8a") then + arch = "arm64" + elseif target:is_arch("mips") then + arch = "mips" + elseif target:is_arch("ppc", "ppc64", "powerpc", "powerpc64") then + arch = "powerpc" + end + assert(arch, "unknown arch(%s)!", target:arch()) + local cc = target:tool("cc") + local cross = cc:gsub("%-gcc$", "-") + table.insert(argv, "ARCH=" .. arch) + table.insert(argv, "CROSS_COMPILE=" .. cross) + end + local result, errors = try {function () return os.iorunv(make.program, argv, {curdir = tmpdir}) end} + if result then + for _, line in ipairs(result:split("\n", {plain = true})) do + if line:endswith("stub.c") then + local include_cflag = false + for _, cflag in ipairs(line:split("%s+")) do + local has_cflag = false + if cflag:startswith("-f") or cflag:startswith("-m") + or (cflag:startswith("-W") and not cflag:startswith("-Wp,-MMD,") and not cflag:startswith("-Wp,-MD,")) + or (cflag:startswith("-D") and not cflag:find("KBUILD_MODNAME=") and not cflag:find("KBUILD_BASENAME=")) then + has_cflag = true + local macro = cflag:match("%-D\"(.+)\"") -- -D"KBUILD_XXX=xxx" + if macro then + cflag = "-D" .. macro + end + elseif cflag == "-I" or cflag == "-isystem" or cflag == "-include" then + include_cflag = cflag + elseif cflag:startswith("-I") or include_cflag then + local includedir = cflag + if cflag:startswith("-I") then + includedir = cflag:sub(3) + end + if not path.is_absolute(includedir) then + includedir = path.absolute(includedir, sdkdir) + end + if cflag:startswith("-I") then + cflag = "-I" .. includedir + else + cflag = include_cflag .. " " .. includedir + end + has_cflag = true + include_cflag = nil + end + if has_cflag then + cflags = cflags or {} + table.insert(cflags, cflag) + end + end + end + local ldflags = line:match("%-ld (.+) %-o ") or line:match("ld (.+) %-o ") + if ldflags then + local ko = ldflags:find("-T ", 1, true) + for _, ldflag in ipairs(os.argv(ldflags)) do + if ldflag:endswith(".lds") then + if not path.is_absolute(ldflag) then + ldflag = path.absolute(ldflag, sdkdir) + end + end + if ko then + -- e.g. aarch64-linux-gnu-ld -r -EL -maarch64elf --build-id=sha1 -T scripts/module.lds -o hello.ko hello.o hello.mod.o + ldflags_ko = ldflags_ko or {} + table.insert(ldflags_ko, ldflag) + else + -- e.g. aarch64-linux-gnu-ld -EL -maarch64elf -r -o hello.o xxx.o + ldflags_o = ldflags_o or {} + table.insert(ldflags_o, ldflag) + end + end + end + if cflags and ldflags_o and ldflags_ko then + break + end + end + else + if option.get("diagnosis") then + print("rule(platform.linux.driver): cannot get cflags from make!") + print(errors) + end + end + os.tryrm(tmpdir) + memcache.set2("linux.driver", key, "cflags", cflags or false) + end + return cflags or nil, ldflags_o or nil, ldflags_ko or nil +end + +function load(target) + -- we need only need binary kind, because we will rewrite on_link + target:set("kind", "binary") + target:set("extension", ".ko") +end + +function config(target) + + -- get and save linux-headers sdk + local linux_headers = _get_linux_headers_sdk(target) + target:data_set("linux.driver.linux_headers", linux_headers) + + -- check compiler, we must use gcc + assert(target:has_tool("cc", "gcc"), "we must use gcc compiler!") + + -- check rules + for _, rulename in ipairs({"mode.release", "mode.debug", "mode.releasedbg", "mode.minsizerel", "mode.asan", "mode.tsan"}) do + assert(not target:rule(rulename), "target(%s) is linux driver module, it need not rule(%s)!", target:name(), rulename) + end + + -- we need disable includedirs from add_packages("linux-headers") + if target:pkg("linux-headers") then + target:pkg("linux-headers"):set("includedirs", nil) + target:pkg("linux-headers"):set("sysincludedirs", nil) + end + + -- add compilation flags + target:add("defines", "KBUILD_MODNAME=\"" .. target:name() .. "\"") + for _, sourcefile in ipairs(target:sourcefiles()) do + target:fileconfig_set(sourcefile, {defines = "KBUILD_BASENAME=\"" .. path.basename(sourcefile) .. "\""}) + end + local cflags, ldflags_o, ldflags_ko = _get_cflags_from_make(target, linux_headers.sdkdir) + if cflags then + target:add("cflags", cflags, {force = true}) + target:data_set("linux.driver.ldflags_o", ldflags_o) + target:data_set("linux.driver.ldflags_ko", ldflags_ko) + end +end + +function link(target, opt) + local targetfile = target:targetfile() + local dependfile = target:dependfile(targetfile) + local objectfiles = target:objectfiles() + depend.on_changed(function () + + -- trace + progress.show(opt.progress, "${color.build.object}linking.$(mode) %s", targetfile) + + -- get module scripts + local modpost + local linux_headers = target:data("linux.driver.linux_headers") + if linux_headers then + modpost = path.join(linux_headers.sdkdir, "scripts", "mod", "modpost") + end + assert(modpost and os.isfile(modpost), "scripts/mod/modpost not found!") + + -- get ld + local ld = target:tool("ld") + assert(ld, "ld not found!") + ld = ld:gsub("gcc$", "ld") + ld = ld:gsub("g%+%+$", "ld") + + -- link target.o + local argv = {} + local ldflags_o = target:data("linux.driver.ldflags_o") + if ldflags_o then + table.join2(argv, ldflags_o) + end + local targetfile_o = target:objectfile(targetfile) + table.join2(argv, "-o", targetfile_o) + table.join2(argv, objectfiles) + os.mkdir(path.directory(targetfile_o)) + os.vrunv(ld, argv) + + -- generate target.mod + local targetfile_mod = targetfile_o:gsub("%.o$", ".mod") + io.writefile(targetfile_mod, table.concat(objectfiles, " ") .. "\n\n") + + -- generate .sourcename.o.cmd + -- we need only touch an empty file, otherwise modpost command will raise error. + for _, objectfile in ipairs(objectfiles) do + local objectdir = path.directory(objectfile) + local objectname = path.filename(objectfile) + local cmdfile = path.join(objectdir, "." .. objectname .. ".cmd") + io.writefile(cmdfile, "") + end + + -- generate target.mod.c + local orderfile = path.join(path.directory(targetfile_o), "modules.order") + local symversfile = path.join(path.directory(targetfile_o), "Module.symvers") + argv = {"-m", "-a", "-o", symversfile, "-e", "-N", "-T", "-"} + io.writefile(orderfile, targetfile_o .. "\n") + os.vrunv(modpost, argv, {stdin = orderfile}) + + -- compile target.mod.c + local targetfile_mod_c = targetfile_o:gsub("%.o$", ".mod.c") + local targetfile_mod_o = targetfile_o:gsub("%.o$", ".mod.o") + local compinst = target:compiler("cc") + if option.get("verbose") then + print(compinst:compcmd(targetfile_mod_c, targetfile_mod_o, {target = target, rawargs = true})) + end + assert(compinst:compile(targetfile_mod_c, targetfile_mod_o, {target = target})) + + -- link target.ko + argv = {} + local ldflags_ko = target:data("linux.driver.ldflags_ko") + if ldflags_ko then + table.join2(argv, ldflags_ko) + end + local targetfile_o = target:objectfile(targetfile) + table.join2(argv, "-o", targetfile, targetfile_o, targetfile_mod_o) + os.mkdir(path.directory(targetfile)) + os.vrunv(ld, argv) + + end, {dependfile = dependfile, lastmtime = os.mtime(target:targetfile()), files = objectfiles}) +end diff --git a/xmake/rules/platform/linux/driver/xmake.lua b/xmake/rules/platform/linux/driver/xmake.lua new file mode 100644 index 000000000..8192b9a7f --- /dev/null +++ b/xmake/rules/platform/linux/driver/xmake.lua @@ -0,0 +1,33 @@ +--!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 xmake.lua +-- + +-- build linux driver module +rule("platform.linux.driver") + set_sourcekinds("cc") + on_load(function (target) + import("driver_modules").load(target) + end) + on_config(function (target) + import("driver_modules").config(target) + end) + on_link(function (target, opt) + import("driver_modules").link(target, opt) + end) + diff --git a/xmake/rules/platform/windows/def/xmake.lua b/xmake/rules/platform/windows/def/xmake.lua index 06e960380..7919553de 100644 --- a/xmake/rules/platform/windows/def/xmake.lua +++ b/xmake/rules/platform/windows/def/xmake.lua @@ -22,8 +22,7 @@ rule("platform.windows.def") set_extensions(".def") on_config("windows", function (target) - local _, toolname = target:tool("ld") - if toolname == "link" then + if target:has_tool("ld", "link") then for _, sourcebatch in pairs(target:sourcebatches()) do if sourcebatch.rulename == "platform.windows.def" then for _, sourcefile in ipairs(sourcebatch.sourcefiles) do diff --git a/xmake/rules/platform/windows/manifest/xmake.lua b/xmake/rules/platform/windows/manifest/xmake.lua index efb750edd..b476faeed 100644 --- a/xmake/rules/platform/windows/manifest/xmake.lua +++ b/xmake/rules/platform/windows/manifest/xmake.lua @@ -23,8 +23,7 @@ rule("platform.windows.manifest") set_extensions(".manifest") on_config("windows", function (target) - local _, toolname = target:tool("ld") - if toolname == "link" then + if target:has_tool("ld", "link") then local manifest = false for _, sourcebatch in pairs(target:sourcebatches()) do if sourcebatch.rulename == "platform.windows.manifest" then diff --git a/xmake/rules/plugin/vsxmake/xmake.lua b/xmake/rules/plugin/vsxmake/xmake.lua index 88693599f..bb0f231e4 100644 --- a/xmake/rules/plugin/vsxmake/xmake.lua +++ b/xmake/rules/plugin/vsxmake/xmake.lua @@ -35,12 +35,17 @@ rule("plugin.vsxmake.autoupdate") import("core.project.config") import("core.project.depend") import("core.project.project") + import("core.cache.localcache") import("core.base.task") -- run only once for all xmake process in vs local tmpfile = path.join(config.buildir(), ".gens", "rules", "plugin.vsxmake.autoupdate") local dependfile = tmpfile .. ".d" local lockfile = io.openlock(tmpfile .. ".lock") + local kind = localcache.get("vsxmake", "kind") + local modes = localcache.get("vsxmake", "modes") + local archs = localcache.get("vsxmake", "archs") + local outputdir = localcache.get("vsxmake", "outputdir") if lockfile:trylock() then if os.getenv("XMAKE_IN_VSTUDIO") then local sourcefiles = {} @@ -50,8 +55,8 @@ rule("plugin.vsxmake.autoupdate") table.sort(sourcefiles) depend.on_changed(function () -- we use task instead of os.exec("xmake") to avoid the project lock - print("update vsxmake project ..") - task.run("project", {kind = "vsxmake"}) + print("update vsxmake project -k %s %s ..", kind or "vsxmake", outputdir or "") + task.run("project", {kind = kind or "vsxmake", modes = modes, archs = archs, outputdir = outputdir}) print("update vsxmake project ok") end, {dependfile = dependfile, files = project.allfiles(), diff --git a/xmake/rules/python/xmake.lua b/xmake/rules/python/xmake.lua new file mode 100644 index 000000000..823495485 --- /dev/null +++ b/xmake/rules/python/xmake.lua @@ -0,0 +1,43 @@ +--!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 xmake.lua +-- + +-- @see https://github.com/xmake-io/xmake/issues/1896 +rule("python.library") + on_load(function (target) + target:set("kind", "shared") + target:set("prefixname", "_") + local soabi = target:extraconf("rules", "python.library", "soabi") + if soabi then + import("lib.detect.find_tool") + local python = assert(find_tool("python3"), "python not found!") + local result = try { function() return os.iorunv(python.program, {"-c", "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))"}) end} + if result then + result = result:trim() + if result ~= "None" then + target:set("extension", result) + end + end + else + if target:is_plat("windows") then + target:set("extension", ".pyd") + end + end + end) + diff --git a/xmake/rules/qt/deploy/android.lua b/xmake/rules/qt/deploy/android.lua index 97f97ea7a..326addbc1 100644 --- a/xmake/rules/qt/deploy/android.lua +++ b/xmake/rules/qt/deploy/android.lua @@ -25,7 +25,7 @@ import("core.base.semver") import("core.project.config") import("core.project.depend") import("core.tool.toolchain") -import("private.utils.progress") +import("utils.progress") -- escape path function _escape_path(p) @@ -71,6 +71,9 @@ function main(target, opt) -- get androiddeployqt local androiddeployqt = path.join(qt.bindir, "androiddeployqt" .. (is_host("windows") and ".exe" or "")) + if not os.isexec(androiddeployqt) and qt.bindir_host then + androiddeployqt = path.join(qt.bindir_host, "androiddeployqt" .. (is_host("windows") and ".exe" or "")) + end assert(os.isexec(androiddeployqt), "androiddeployqt not found!") -- get working directory @@ -120,18 +123,6 @@ function main(target, opt) os.cp(target:targetfile(), path.join(android_buildir, "libs", target_arch, path.filename(target:targetfile()))) end - -- get the android srcs directory, e.g. android-build/java/res/values - local android_srcs - if qt_sdkver and qt_sdkver:ge("5.14") then - -- @note we need patch values/res/strings.xml for Qt 5.14.0 - local valuesdir = path.join(android_buildir, "java", "res", "values") - if not os.isdir(valuesdir) then - os.mkdir(valuesdir) - end - os.cp(path.join(qt.sdkdir, "src", "android", "java", "res", "values", "*"), valuesdir) - android_srcs = path.join(android_buildir, "java") - end - -- get stdcpp path local stdcpp_path = path.join(ndk, "sources/cxx-stl/llvm-libc++/libs", target_arch, "libc++_shared.so") if qt_sdkver and qt_sdkver:ge("5.14") then @@ -159,9 +150,21 @@ function main(target, opt) settings_file:print(' "ndk-host": "%s",', ndk_host) settings_file:print(' "target-architecture": "%s",', target_arch) settings_file:print(' "qml-root-path": "%s",', _escape_path(os.projectdir())) - if android_srcs then - settings_file:print(' "android-package-source-directory": "%s",', _escape_path(android_srcs)) - --settings_file:print(' "android-extra-libs":"c:/libs",') + -- for 6.2.x + local qmlimportscanner = path.join(qt.libexecdir, "qmlimportscanner" .. (is_host("windows") and ".exe" or "")) + if not os.isexec(qmlimportscanner) and qt.libexecdir_host then + qmlimportscanner = path.join(qt.libexecdir_host, "qmlimportscanner" .. (is_host("windows") and ".exe" or "")) + end + if os.isexec(qmlimportscanner) then + settings_file:print(' "qml-importscanner-binary": "%s",', qmlimportscanner) + end + local minsdkversion = target:values("qt.android.minsdkversion") + if minsdkversion then + settings_file:print(' "android-min-sdk-version": "%s",', tostring(minsdkversion)) + end + local targetsdkversion = target:values("qt.android.targetsdkversion") + if targetsdkversion then + settings_file:print(' "android-target-sdk-version": "%s",', tostring(targetsdkversion)) end settings_file:print(' "useLLVM": true,') if qt_sdkver and qt_sdkver:ge("5.14") then @@ -194,7 +197,6 @@ function main(target, opt) -- do deploy local argv = {"--input", android_deployment_settings, "--output", android_buildir, - "--android-platform", android_platform, "--jdk", java_home, "--gradle", "--no-gdbserver"} if option.get("verbose") and option.get("diagnosis") then diff --git a/xmake/rules/qt/deploy/macosx.lua b/xmake/rules/qt/deploy/macosx.lua index 5f7b8390d..26df10b14 100644 --- a/xmake/rules/qt/deploy/macosx.lua +++ b/xmake/rules/qt/deploy/macosx.lua @@ -25,7 +25,7 @@ import("core.project.config") import("core.project.depend") import("core.tool.toolchain") import("lib.detect.find_path") -import("private.utils.progress") +import("utils.progress") -- save Info.plist function _save_info_plist(target, info_plist_file) diff --git a/xmake/rules/qt/load.lua b/xmake/rules/qt/load.lua index 03599f7e2..c5bec10af 100644 --- a/xmake/rules/qt/load.lua +++ b/xmake/rules/qt/load.lua @@ -36,7 +36,11 @@ function _link(linkdirs, framework, qt_sdkver) elseif is_plat("android") or is_plat("linux") then debug_suffix = "" end - framework = "Qt" .. qt_sdkver:major() .. framework:sub(3) .. (is_mode("debug") and debug_suffix or "") + if qt_sdkver:ge("5.0") then + framework = "Qt" .. qt_sdkver:major() .. framework:sub(3) .. (is_mode("debug") and debug_suffix or "") + else -- for qt4.x, e.g. QtGui4.lib + framework = "Qt" .. framework:sub(3) .. (is_mode("debug") and debug_suffix or "") .. qt_sdkver:major() + end if is_plat("android") then --> -lQt5Core_armeabi/-lQt5CoreDebug_armeabi for 5.14.x local libinfo = find_library(framework .. "_" .. config.arch(), linkdirs) if libinfo and libinfo.link then @@ -77,10 +81,10 @@ function _add_plugins(target, plugins) for name, plugin in pairs(plugins) do target:values_add("qt.plugins", name) if plugin.links then - target:values_add("qt.links", unpack(table.wrap(plugin.links))) + target:values_add("qt.links", table.unpack(table.wrap(plugin.links))) end if plugin.linkdirs then - target:values_add("qt.linkdirs", unpack(table.wrap(plugin.linkdirs))) + target:values_add("qt.linkdirs", table.unpack(table.wrap(plugin.linkdirs))) end end end diff --git a/xmake/rules/qt/moc/xmake.lua b/xmake/rules/qt/moc/xmake.lua index 80ce37174..f12fcf217 100644 --- a/xmake/rules/qt/moc/xmake.lua +++ b/xmake/rules/qt/moc/xmake.lua @@ -32,6 +32,9 @@ rule("qt.moc") if not os.isexec(moc) and qt.libexecdir then moc = path.join(qt.libexecdir, is_host("windows") and "moc.exe" or "moc") end + if not os.isexec(moc) and qt.libexecdir_host then + moc = path.join(qt.libexecdir_host, is_host("windows") and "moc.exe" or "moc") + end assert(moc and os.isexec(moc), "moc not found!") -- get c++ source file for moc diff --git a/xmake/rules/qt/qrc/xmake.lua b/xmake/rules/qt/qrc/xmake.lua index df416c2a9..64e37d2ab 100644 --- a/xmake/rules/qt/qrc/xmake.lua +++ b/xmake/rules/qt/qrc/xmake.lua @@ -29,6 +29,9 @@ rule("qt.qrc") if not os.isexec(rcc) and qt.libexecdir then rcc = path.join(qt.libexecdir, is_host("windows") and "rcc.exe" or "rcc") end + if not os.isexec(rcc) and qt.libexecdir_host then + rcc = path.join(qt.libexecdir_host, is_host("windows") and "rcc.exe" or "rcc") + end assert(os.isexec(rcc), "rcc not found!") -- save rcc diff --git a/xmake/rules/qt/ui/xmake.lua b/xmake/rules/qt/ui/xmake.lua index dd377563b..2d02a134b 100644 --- a/xmake/rules/qt/ui/xmake.lua +++ b/xmake/rules/qt/ui/xmake.lua @@ -29,6 +29,9 @@ rule("qt.ui") if not os.isexec(uic) and qt.libexecdir then uic = path.join(qt.libexecdir, is_host("windows") and "uic.exe" or "uic") end + if not os.isexec(uic) and qt.libexecdir_host then + uic = path.join(qt.libexecdir_host, is_host("windows") and "uic.exe" or "uic") + end assert(uic and os.isexec(uic), "uic not found!") -- add includedirs, @note we need create this directory first to suppress warning (file not found). diff --git a/xmake/rules/qt/xmake.lua b/xmake/rules/qt/xmake.lua index ed4deedb7..b26a458a5 100644 --- a/xmake/rules/qt/xmake.lua +++ b/xmake/rules/qt/xmake.lua @@ -87,7 +87,20 @@ rule("qt.widgetapp") end) on_config(function (target) - import("load")(target, {gui = true, frameworks = {"QtGui", "QtWidgets", "QtCore"}}) + + -- get qt sdk version + local qt = target:data("qt") + local qt_sdkver = nil + if qt.sdkver then + import("core.base.semver") + qt_sdkver = semver.new(qt.sdkver) + end + + local frameworks = {"QtGui", "QtWidgets", "QtCore"} + if qt_sdkver and qt_sdkver:lt("5.0") then + frameworks = {"QtGui", "QtCore"} -- qt4.x has not QtWidgets, it is in QtGui + end + import("load")(target, {gui = true, frameworks = frameworks}) end) -- deploy application @@ -124,9 +137,12 @@ rule("qt.widgetapp_static") QtPlatformSupport = "QtPlatformCompositorSupport" end - -- laod some basic plugins and frameworks + -- load some basic plugins and frameworks local plugins = {} local frameworks = {"QtGui", "QtWidgets", "QtCore"} + if qt_sdkver and qt_sdkver:lt("5.0") then + frameworks = {"QtGui", "QtCore"} -- qt4.x has not QtWidgets, it is in QtGui + end if target:is_plat("macosx") then plugins.QCocoaIntegrationPlugin = {linkdirs = "plugins/platforms", links = {"qcocoa", "cups"}} table.join2(frameworks, QtPlatformSupport, "QtWidgets") @@ -195,7 +211,7 @@ rule("qt.quickapp_static") QtPlatformSupport = "QtPlatformCompositorSupport" end - -- laod some basic plugins and frameworks + -- load some basic plugins and frameworks local plugins = {} local frameworks = {"QtGui", "QtQuick", "QtQml", "QtQmlModels", "QtCore", "QtNetwork"} if target:is_plat("macosx") then diff --git a/xmake/rules/rust/build/cxxbridge.lua b/xmake/rules/rust/build/cxxbridge.lua new file mode 100644 index 000000000..1fc9b3778 --- /dev/null +++ b/xmake/rules/rust/build/cxxbridge.lua @@ -0,0 +1,50 @@ +--!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 cxxbridge.lua +-- + +-- imports +import("core.base.option") +import("lib.detect.find_tool") + +function main(target, batchcmds, sourcefile, opt) + local cxxbridge = assert(find_tool("cxxbridge"), "cxxbridge not found, please run `cargo install cxxbridge` to install it first!") + + -- get c/c++ source file for cxxbridge + local headerfile = path.join(target:autogendir(), "rules", "cxxbridge", path.basename(sourcefile) .. ".rs.h") + local sourcefile_cx = path.join(target:autogendir(), "rules", "cxxbridge", path.basename(sourcefile) .. ".rs.cc") + + -- add includedirs + target:add("includedirs", path.directory(headerfile)) + + -- add objectfile + local objectfile = target:objectfile(sourcefile_cx) + table.insert(target:objectfiles(), objectfile) + + -- add commands + batchcmds:show_progress(opt.progress, "${color.build.object}compiling.cxxbridge %s", sourcefile) + batchcmds:mkdir(path.directory(sourcefile_cx)) + batchcmds:vrunv(cxxbridge.program, {sourcefile}, {stdout = sourcefile_cx}) + batchcmds:vrunv(cxxbridge.program, {sourcefile, "--header"}, {stdout = headerfile}) + batchcmds:compile(sourcefile_cx, objectfile) + + -- add deps + batchcmds:add_depfiles(sourcefile) + batchcmds:set_depmtime(os.mtime(objectfile)) + batchcmds:set_depcache(target:dependfile(objectfile)) +end diff --git a/xmake/rules/rust/build/target.lua b/xmake/rules/rust/build/target.lua index 67917ad29..5f0167693 100644 --- a/xmake/rules/rust/build/target.lua +++ b/xmake/rules/rust/build/target.lua @@ -21,19 +21,13 @@ -- imports import("core.base.option") import("core.base.hashset") -import("core.theme.theme") import("core.tool.compiler") import("core.project.depend") +import("utils.progress") -- build the source files function build_sourcefiles(target, sourcebatch, opt) - -- is verbose? - local verbose = option.get("verbose") - - -- get progress range - local progress = assert(opt.progress, "no progress!") - -- get the target file local targetfile = target:targetfile() @@ -60,17 +54,10 @@ function build_sourcefiles(target, sourcebatch, opt) end -- trace progress into - cprintf("${color.build.progress}" .. theme.get("text.build.progress_format") .. ":${clear} ", progress) - if verbose then - cprint("${dim color.build.target}linking.$(mode) %s", path.filename(targetfile)) - else - cprint("${color.build.target}linking.$(mode) %s", path.filename(targetfile)) - end + progress.show(opt.progress, "${color.build.target}linking.$(mode) %s", path.filename(targetfile)) -- trace verbose info - if verbose then - print(compinst:buildcmd(sourcefiles, targetfile, {target = target, compflags = compflags})) - end + vprint(compinst:buildcmd(sourcefiles, targetfile, {target = target, compflags = compflags})) -- flush io buffer to update progress info io.flush() @@ -89,10 +76,11 @@ end function main(target, opt) -- @note only support one source kind! - for _, sourcebatch in pairs(target:sourcebatches()) do - if sourcebatch.sourcekind == "rc" then + local sourcebatches = target:sourcebatches() + if sourcebatches then + local sourcebatch = sourcebatches["rust.build"] + if sourcebatch then build_sourcefiles(target, sourcebatch, opt) - break end end end diff --git a/xmake/rules/rust/xmake.lua b/xmake/rules/rust/xmake.lua index a8ccae429..e31e2f046 100644 --- a/xmake/rules/rust/xmake.lua +++ b/xmake/rules/rust/xmake.lua @@ -18,16 +18,52 @@ -- @file xmake.lua -- --- define rule: rust.build +-- generate bridge.rs.cc/h to call rust library in c++ code +-- @see https://cxx.rs/build/other.html +rule("rust.cxxbridge") + set_extensions(".rsx") + on_load(function (target) + if not target:get("languages") then + target:set("languages", "c++11") + end + end) + before_buildcmd_file("build.cxxbridge") + rule("rust.build") set_sourcekinds("rc") + on_load(function (target) + -- set cratetype + local cratetype = target:values("rust.cratetype") + if cratetype == "staticlib" then + assert(target:is_static(), "target(%s) must be static kind for cratetype(staticlib)!", target:name()) + target:add("arflags", "--crate-type=staticlib") + target:data_set("inherit.links.exportlinks", false) + elseif cratetype == "cdylib" then + assert(target:is_shared(), "target(%s) must be shared kind for cratetype(cdylib)!", target:name()) + target:add("shflags", "--crate-type=cdylib") + target:add("shflags", "-C prefer-dynamic") + elseif target:is_static() then + target:set("extension", ".rlib") + target:add("arflags", "--crate-type=lib") + target:data_set("inherit.links.deplink", false) + elseif target:is_shared() then + target:add("shflags", "--crate-type=dylib") + -- fix cannot satisfy dependencies so `std` only shows up once + -- https://github.com/rust-lang/rust/issues/19680 + -- + -- but it will link dynamic @rpath/libstd-xxx.dylib, + -- so we can no longer modify and set other rpath paths + target:add("shflags", "-C prefer-dynamic") + elseif target:is_binary() then + target:add("ldflags", "--crate-type=bin") + end + + -- set edition + local edition = target:values("rust.edition") or "2018" + target:add("rcflags", "--edition", edition, {force = true}) + end) on_build("build.target") --- define rule: rust rule("rust") - - -- add build rules add_deps("rust.build") - - -- inherit links and linkdirs of all dependent targets by default add_deps("utils.inherit.links") diff --git a/xmake/rules/swig/build_module_file.lua b/xmake/rules/swig/build_module_file.lua new file mode 100644 index 000000000..42c9d9238 --- /dev/null +++ b/xmake/rules/swig/build_module_file.lua @@ -0,0 +1,55 @@ +--!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_module_file.lua +-- + +-- imports +import("lib.detect.find_tool") + +function main(target, batchcmds, sourcefile, opt) + + -- get swig + opt = opt or {} + local swig = assert(find_tool("swig"), "swig not found!") + local sourcefile_cx = path.join(target:autogendir(), "rules", "swig", path.basename(sourcefile) .. (opt.sourcekind == "cxx" and ".cpp" or ".c")) + + -- add objectfile + local objectfile = target:objectfile(sourcefile_cx) + table.insert(target:objectfiles(), objectfile) + + -- add commands + local moduletype = assert(target:data("swig.moduletype"), "swig.moduletype not found!") + local argv = {"-" .. moduletype, "-o", sourcefile_cx} + if opt.sourcekind == "cxx" then + table.insert(argv, "-c++") + end + local fileconfig = target:fileconfig(sourcefile) + if fileconfig.swigflags then + table.join2(argv, fileconfig.swigflags) + end + table.insert(argv, sourcefile) + batchcmds:show_progress(opt.progress, "${color.build.object}compiling.swig.%s %s", moduletype, sourcefile) + batchcmds:mkdir(path.directory(sourcefile_cx)) + batchcmds:vrunv(swig.program, argv) + batchcmds:compile(sourcefile_cx, objectfile) + + -- add deps + batchcmds:add_depfiles(sourcefile) + batchcmds:set_depmtime(os.mtime(objectfile)) + batchcmds:set_depcache(target:dependfile(objectfile)) +end diff --git a/xmake/rules/swig/xmake.lua b/xmake/rules/swig/xmake.lua new file mode 100644 index 000000000..c0387a940 --- /dev/null +++ b/xmake/rules/swig/xmake.lua @@ -0,0 +1,98 @@ +--!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 xmake.lua +-- + +-- references: +-- +-- https://github.com/xmake-io/xmake/issues/1622 +-- http://www.swig.org/Doc4.0/SWIGDocumentation.html#Introduction_nn4 +-- + +rule("swig.base") + on_load(function (target) + target:set("kind", "shared") + local moduletype = target:extraconf("rules", "swig.c", "moduletype") or target:extraconf("rules", "swig.cpp", "moduletype") + if moduletype == "python" then + target:set("prefixname", "_") + local soabi = target:extraconf("rules", "swig.c", "soabi") or target:extraconf("rules", "swig.cpp", "soabi") + if soabi then + import("lib.detect.find_tool") + local python = assert(find_tool("python3"), "python not found!") + local result = try { function() return os.iorunv(python.program, {"-c", "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX'))"}) end} + if result then + result = result:trim() + if result ~= "None" then + target:set("extension", result) + end + end + else + if target:is_plat("windows") then + target:set("extension", ".pyd") + end + end + elseif moduletype == "lua" then + target:set("prefixname", "") + if not target:is_plat("windows") then + target:set("extension", ".so") + end + else + raise("unknown swig module type, please use `add_rules(\"swig.c\", {moduletype = \"python\"})` to set it!") + end + local scriptfiles = {} + for _, sourcebatch in pairs(target:sourcebatches()) do + if sourcebatch.rulename:startswith("swig.") then + for _, sourcefile in ipairs(sourcebatch.sourcefiles) do + local scriptdir + local fileconfig = target:fileconfig(sourcefile) + if fileconfig then + scriptdir = fileconfig.scriptdir + end + local scriptfile = path.join(target:autogendir(), "rules", "swig", path.basename(sourcefile)) + if moduletype == "python" then + scriptfile = scriptfile .. ".py" + elseif moduletype == "lua" then + scriptfile = scriptfile .. ".lua" + end + table.insert(scriptfiles, scriptfile) + if scriptdir then + target:add("installfiles", scriptfile, {prefixdir = scriptdir}) + end + end + end + end + -- for custom on_install/after_install, user can use it to install them + target:data_set("swig.scriptfiles", scriptfiles) + target:data_set("swig.moduletype", moduletype) + end) + +rule("swig.c") + set_extensions(".i") + add_deps("swig.base", "c.build") + on_buildcmd_file(function (target, batchcmds, sourcefile, opt) + import("build_module_file")(target, batchcmds, sourcefile, table.join({sourcekind = "cc"}, opt)) + end) + +rule("swig.cpp") + set_extensions(".i") + add_deps("swig.base", "c++.build") + on_buildcmd_file(function (target, batchcmds, sourcefile, opt) + import("build_module_file")(target, batchcmds, sourcefile, table.join({sourcekind = "cxx"}, opt)) + end) + + diff --git a/xmake/rules/utils/bin2c/xmake.lua b/xmake/rules/utils/bin2c/xmake.lua index 12db55192..77f81125c 100644 --- a/xmake/rules/utils/bin2c/xmake.lua +++ b/xmake/rules/utils/bin2c/xmake.lua @@ -20,10 +20,17 @@ rule("utils.bin2c") set_extensions(".bin") + on_load(function (target) + local headerdir = path.join(target:autogendir(), "rules", "utils", "bin2c") + if not os.isdir(headerdir) then + os.mkdir(headerdir) + end + target:add("includedirs", headerdir) + end) before_buildcmd_file(function (target, batchcmds, sourcefile_bin, opt) -- get header file - local headerdir = path.join(target:autogendir(), "rules", "c++", "bin2c") + local headerdir = path.join(target:autogendir(), "rules", "utils", "bin2c") local headerfile = path.join(headerdir, path.filename(sourcefile_bin) .. ".h") target:add("includedirs", headerdir) @@ -36,7 +43,7 @@ rule("utils.bin2c") table.insert(argv, "-w") table.insert(argv, tostring(linewidth)) end - batchcmds:vrunv(os.programfile(), argv) + batchcmds:vrunv(os.programfile(), argv, {envs = {XMAKE_SKIP_HISTORY = "y"}}) -- add deps batchcmds:add_depfiles(sourcefile_bin) diff --git a/xmake/rules/utils/glsl2spv/xmake.lua b/xmake/rules/utils/glsl2spv/xmake.lua new file mode 100644 index 000000000..faf655ec6 --- /dev/null +++ b/xmake/rules/utils/glsl2spv/xmake.lua @@ -0,0 +1,90 @@ +--!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 xmake.lua +-- + +-- compile glsl shader to spirv file, .spv +-- +-- e.g. +-- compile *.vert/*.frag to *.vert.spv/*.frag.spv files +-- add_rules("utils.glsl2spv", {outputdir = "build"}) +-- +-- compile *.vert/*.frag and generate binary c header files +-- add_rules("utils.glsl2spv", {bin2c = true}) +-- +-- in c code: +-- static unsigned char g_test_frag_spv_data[] = { +-- #include "test.frag.spv.h" +-- }; +-- +-- +rule("utils.glsl2spv") + set_extensions(".vert", ".frag", ".tesc", ".tese", ".geom", ".comp", ".glsl") + on_load(function (target) + local is_bin2c = target:extraconf("rules", "utils.glsl2spv", "bin2c") + if is_bin2c then + local headerdir = path.join(target:autogendir(), "rules", "utils", "glsl2spv") + if not os.isdir(headerdir) then + os.mkdir(headerdir) + end + target:add("includedirs", headerdir) + end + end) + before_buildcmd_file(function (target, batchcmds, sourcefile_glsl, opt) + import("lib.detect.find_tool") + + -- get glslangValidator + local glslc + local glslangValidator = find_tool("glslangValidator") + if not glslangValidator then + glslc = find_tool("glslc") + end + assert(glslangValidator or glslc, "glslangValidator or glslc not found!") + + -- glsl to spv + local outputdir = target:extraconf("rules", "utils.glsl2spv", "outputdir") or path.join(target:autogendir(), "rules", "utils", "glsl2spv") + local spvfilepath = path.join(outputdir, path.filename(sourcefile_glsl) .. ".spv") + batchcmds:show_progress(opt.progress, "${color.build.object}generating.glsl2spv %s", sourcefile_glsl) + batchcmds:mkdir(outputdir) + if glslangValidator then + batchcmds:vrunv(glslangValidator.program, {"-V", "-o", spvfilepath, sourcefile_glsl}) + else + batchcmds:vrunv(glslc.program, {"-o", spvfilepath, sourcefile_glsl}) + end + + -- do bin2c + local outputfile = spvfilepath + local is_bin2c = target:extraconf("rules", "utils.glsl2spv", "bin2c") + if is_bin2c then + -- get header file + local headerdir = outputdir + local headerfile = path.join(headerdir, path.filename(spvfilepath) .. ".h") + target:add("includedirs", headerdir) + outputfile = headerfile + + -- add commands + local argv = {"lua", "private.utils.bin2c", "--nozeroend", "-i", spvfilepath, "-o", headerfile} + batchcmds:vrunv(os.programfile(), argv, {envs = {XMAKE_SKIP_HISTORY = "y"}}) + end + + -- add deps + batchcmds:add_depfiles(sourcefile_glsl) + batchcmds:set_depmtime(os.mtime(outputfile)) + batchcmds:set_depcache(target:dependfile(outputfile)) + end) + diff --git a/xmake/rules/utils/inherit_links/inherit_links.lua b/xmake/rules/utils/inherit_links/inherit_links.lua index 97c585ded..79c737503 100644 --- a/xmake/rules/utils/inherit_links/inherit_links.lua +++ b/xmake/rules/utils/inherit_links/inherit_links.lua @@ -46,7 +46,6 @@ function _add_export_value(target, name, value) end end --- main entry function main(target) -- disable inherit.links for `add_deps()`? @@ -59,12 +58,15 @@ function main(target) if targetkind == "shared" or targetkind == "static" then local targetfile = target:targetfile() - -- we need move target link to head - _add_export_value(target, "links", target:linkname()) - local links = target:get("links", {rawref = true}) - if links and type(links) == "table" and #links > 1 then - table.insert(links, 1, links[#links]) - table.remove(links, #links) + -- rust maybe will disable inherit links, only inherit linkdirs + if target:data("inherit.links.deplink") ~= false then + -- we need move target link to head + _add_export_value(target, "links", target:linkname()) + local links = target:get("links", {rawref = true}) + if links and type(links) == "table" and #links > 1 then + table.insert(links, 1, links[#links]) + table.remove(links, #links) + end end _add_export_value(target, "linkdirs", path.directory(targetfile)) @@ -78,11 +80,13 @@ function main(target) -- @note we only export links for static target, -- and we need pass `{public = true}` to add_packages/add_links/... to export it if want to export links for shared target -- - if targetkind == "static" then - for _, name in ipairs({"frameworkdirs", "frameworks", "linkdirs", "links", "syslinks"}) do - local values = _get_values_from_target(target, name) - if values and #values > 0 then - target:add(name, values, {public = true}) + if target:data("inherit.links.exportlinks") ~= false then + if targetkind == "static" then + for _, name in ipairs({"rpathdirs", "frameworkdirs", "frameworks", "linkdirs", "links", "syslinks"}) do + local values = _get_values_from_target(target, name) + if values and #values > 0 then + target:add(name, values, {public = true}) + end end end end diff --git a/xmake/rules/utils/install_importfiles/xmake.lua b/xmake/rules/utils/install_importfiles/xmake.lua index 45e6616f8..848f215e1 100644 --- a/xmake/rules/utils/install_importfiles/xmake.lua +++ b/xmake/rules/utils/install_importfiles/xmake.lua @@ -21,7 +21,9 @@ -- install pkg-config/*.pc import files rule("utils.install.pkgconfig_importfiles") after_install(function (target, opt) - import("target.action.install.pkgconfig_importfiles")(target, opt) + opt = opt or {} + local filename = target:extraconf("rules", "utils.install.pkgconfig_importfiles", "filename") + import("target.action.install.pkgconfig_importfiles")(target, table.join(opt, {filename = filename})) end) -- install *.cmake import files diff --git a/xmake/rules/utils/merge_archive/merge_archive.lua b/xmake/rules/utils/merge_archive/merge_archive.lua index 96069befb..02d572afc 100644 --- a/xmake/rules/utils/merge_archive/merge_archive.lua +++ b/xmake/rules/utils/merge_archive/merge_archive.lua @@ -23,7 +23,7 @@ import("core.base.option") import("core.theme.theme") import("core.project.depend") import("core.project.target", {alias = "project_target"}) -import("private.utils.progress") +import("utils.progress") import("core.tool.toolchain") import("private.tools.vstool") @@ -93,11 +93,12 @@ end -- do extract function _extract(target, libraryfile, objectdir) - local program, toolname = target:tool("ex") + local program, toolname = target:tool("ar") if program and toolname then if toolname:find("ar") then _extract_for_ar(program, libraryfile, objectdir) - elseif toolname == "lib" then + elseif toolname == "link" then + program = program:replace("link.exe", "lib.exe", {plain = true}) _extract_for_msvclib(program, libraryfile, objectdir) end else diff --git a/xmake/rules/utils/merge_archive/xmake.lua b/xmake/rules/utils/merge_archive/xmake.lua index 6d57f55bb..fd7805d8b 100644 --- a/xmake/rules/utils/merge_archive/xmake.lua +++ b/xmake/rules/utils/merge_archive/xmake.lua @@ -18,12 +18,32 @@ -- @file xmake.lua -- --- define rule: utils.merge.archive rule("utils.merge.archive") - - -- set extensions set_extensions(".a", ".lib") - - -- on build file on_build_files("merge_archive") + after_link(function (target, opt) + if target:policy("build.merge_archive") and target:is_static() then + import("utils.archive.merge_staticlib") + import("core.project.depend") + import("utils.progress") + local libraryfiles = {} + for _, dep in ipairs(target:orderdeps()) do + if dep:is_static() then + table.insert(libraryfiles, dep:targetfile()) + end + end + if #libraryfiles > 0 then + table.insert(libraryfiles, target:targetfile()) + end + depend.on_changed(function () + progress.show(opt.progress, "${color.build.target}merging.$(mode) %s", path.filename(target:targetfile())) + if #libraryfiles > 0 then + local tmpfile = os.tmpfile() .. path.extension(target:targetfile()) + merge_staticlib(target, tmpfile, libraryfiles) + os.cp(tmpfile, target:targetfile()) + os.rm(tmpfile) + end + end, {dependfile = target:dependfile(target:targetfile() .. ".merge_archive"), files = libraryfiles}) + end + end) diff --git a/xmake/rules/utils/merge_object/xmake.lua b/xmake/rules/utils/merge_object/xmake.lua index 167e9013e..8bebb5f5c 100644 --- a/xmake/rules/utils/merge_object/xmake.lua +++ b/xmake/rules/utils/merge_object/xmake.lua @@ -31,7 +31,7 @@ rule("utils.merge.object") import("core.base.option") import("core.theme.theme") import("core.project.depend") - import("private.utils.progress") + import("utils.progress") -- get object file local objectfile = target:objectfile(sourcefile_obj) diff --git a/xmake/rules/utils/symbols/export_all/export_all.lua b/xmake/rules/utils/symbols/export_all/export_all.lua index 1a40e9820..f8247a878 100644 --- a/xmake/rules/utils/symbols/export_all/export_all.lua +++ b/xmake/rules/utils/symbols/export_all/export_all.lua @@ -24,7 +24,7 @@ import("core.tool.toolchain") import("core.base.option") import("core.base.hashset") import("core.project.depend") -import("private.utils.progress") +import("utils.progress") -- export all symbols for dynamic library function main (target, opt) @@ -56,8 +56,9 @@ function main (target, opt) local objectsymbols = try { function () return os.iorunv(dumpbin.program, {"/symbols", "/nologo", objectfile}) end } if objectsymbols then for _, line in ipairs(objectsymbols:split('\n', {plain = true})) do + -- https://docs.microsoft.com/en-us/cpp/build/reference/symbols -- 008 00000000 SECT3 notype () External | add - if line:find("External") then + if line:find("External") and not line:find("UNDEF") then local symbol = line:match(".*External%s+| (.*)") if symbol then symbol = symbol:split('%s')[1] diff --git a/xmake/rules/utils/symbols/export_all/xmake.lua b/xmake/rules/utils/symbols/export_all/xmake.lua index f804e3c3e..b808d63a9 100644 --- a/xmake/rules/utils/symbols/export_all/xmake.lua +++ b/xmake/rules/utils/symbols/export_all/xmake.lua @@ -26,7 +26,7 @@ -- @see https://github.com/xmake-io/xmake/issues/1123 -- rule("utils.symbols.export_all") - before_load(function (target) + on_load(function (target) -- @note it only supports windows/dll now assert(target:is_shared(), 'rule("utils.symbols.export_all"): only for shared target(%s)!', target:name()) if target:is_plat("windows") then diff --git a/xmake/rules/utils/symbols/extract/xmake.lua b/xmake/rules/utils/symbols/extract/xmake.lua index f78cab808..dd1f3a1df 100644 --- a/xmake/rules/utils/symbols/extract/xmake.lua +++ b/xmake/rules/utils/symbols/extract/xmake.lua @@ -46,7 +46,7 @@ rule("utils.symbols.extract") import("core.theme.theme") import("core.project.depend") import("core.platform.platform") - import("private.utils.progress") + import("utils.progress") -- get strip local strip = target:tool("strip") diff --git a/xmake/rules/vala/xmake.lua b/xmake/rules/vala/xmake.lua index a2ce76937..6c3d36213 100644 --- a/xmake/rules/vala/xmake.lua +++ b/xmake/rules/vala/xmake.lua @@ -18,7 +18,7 @@ -- @file xmake.lua -- -rule("vala") +rule("vala.build") set_extensions(".vala") on_load(function (target) -- only vala source files? we need patch c source kind for linker @@ -26,6 +26,37 @@ rule("vala") if #sourcekinds == 0 then table.insert(sourcekinds, "cc") end + + -- we disable to build across targets in parallel, because the source files may depend on other target modules + target:set("policy", "build.across_targets_in_parallel", false) + + -- get vapi file + local vapifile = target:data("vala.vapifile") + if not vapifile then + local vapiname = target:values("vala.vapi") + if vapiname then + vapifile = path.join(target:targetdir(), vapiname) + else + vapifile = path.join(target:targetdir(), target:name() .. ".vapi") + end + target:data_set("vala.vapifile", vapifile) + end + + -- get header file + local headerfile = target:data("vala.headerfile") + if not headerfile then + local headername = target:values("vala.header") + if headername then + headerfile = path.join(target:targetdir(), headername) + else + headerfile = path.join(target:targetdir(), target:name() .. ".h") + end + target:data_set("vala.headerfile", headerfile) + end + if headerfile then + target:add("headerfiles", headerfile) + target:add("sysincludedirs", path.directory(headerfile), {public = true}) + end end) before_buildcmd_file(function (target, batchcmds, sourcefile_vala, opt) @@ -52,6 +83,34 @@ rule("vala") table.insert(argv, package) end end + if target:is_binary() then + for _, dep in ipairs(target:orderdeps()) do + if dep:is_shared() or dep:is_static() then + local vapifile = dep:data("vala.vapifile") + if vapifile then + table.join2(argv, vapifile) + end + end + end + else + local vapifile = target:data("vala.vapifile") + if vapifile then + table.insert(argv, "--vapi=" .. vapifile) + end + local headerfile = target:data("vala.headerfile") + if headerfile then + table.insert(argv, "-H") + table.insert(argv, headerfile) + end + end + local vapidir = target:data("vala.vapidir") + if vapidir then + table.insert(argv, "--vapidir=" .. vapidir) + end + local valaflags = target:data("vala.flags") + if valaflags then + table.join2(argv, valaflags) + end table.insert(argv, sourcefile_vala) batchcmds:vrunv(valac.program, argv) batchcmds:compile(sourcefile_c, objectfile) @@ -62,3 +121,54 @@ rule("vala") batchcmds:set_depcache(target:dependfile(objectfile)) end) + after_install(function (target) + if target:is_shared() or target:is_static() then + local vapifile = target:data("vala.vapifile") + if vapifile then + local installdir = target:installdir() + if installdir then + local sharedir = path.join(installdir, "share") + os.mkdir(sharedir) + os.vcp(vapifile, sharedir) + end + end + end + end) + + after_uninstall(function (target) + if target:is_shared() or target:is_static() then + local vapifile = target:data("vala.vapifile") + if vapifile then + local installdir = target:installdir() + if installdir then + os.rm(path.join(installdir, "share", path.filename(vapifile))) + end + end + end + end) + +rule("vala") + + -- add build rules + add_deps("vala.build") + + -- set compiler runtime, e.g. vs runtime + add_deps("utils.compiler.runtime") + + -- inherit links and linkdirs of all dependent targets by default + add_deps("utils.inherit.links") + + -- support `add_files("src/*.o")` and `add_files("src/*.a")` to merge object and archive files to target + add_deps("utils.merge.object", "utils.merge.archive") + + -- we attempt to extract symbols to the independent file and + -- strip self-target binary if `set_symbols("debug")` and `set_strip("all")` are enabled + add_deps("utils.symbols.extract") + + -- check targets + add_deps("utils.check.targets") + + -- check licenses + add_deps("utils.check.licenses") + + diff --git a/xmake/rules/wdk/env/xmake.lua b/xmake/rules/wdk/env/xmake.lua index 6b7690933..a9089dc5e 100644 --- a/xmake/rules/wdk/env/xmake.lua +++ b/xmake/rules/wdk/env/xmake.lua @@ -22,7 +22,7 @@ rule("wdk.env") -- before load - before_load(function (target) + on_load(function (target) -- imports import("os.winver", {alias = "os_winver"}) diff --git a/xmake/rules/wdk/inf/xmake.lua b/xmake/rules/wdk/inf/xmake.lua index fe060a10a..354fbc5a7 100644 --- a/xmake/rules/wdk/inf/xmake.lua +++ b/xmake/rules/wdk/inf/xmake.lua @@ -28,7 +28,7 @@ rule("wdk.inf") set_extensions(".inf", ".inx") -- before load - before_load(function (target) + on_load(function (target) -- imports import("core.project.config") @@ -54,7 +54,7 @@ rule("wdk.inf") import("core.base.option") import("core.theme.theme") import("core.project.depend") - import("private.utils.progress") + import("utils.progress") -- the target file local targetfile = path.join(target:targetdir(), path.basename(sourcefile) .. ".inf") diff --git a/xmake/rules/wdk/man/xmake.lua b/xmake/rules/wdk/man/xmake.lua index 25cce9f34..c5219aa56 100644 --- a/xmake/rules/wdk/man/xmake.lua +++ b/xmake/rules/wdk/man/xmake.lua @@ -28,7 +28,7 @@ rule("wdk.man") set_extensions(".man") -- before load - before_load(function (target) + on_load(function (target) -- imports import("core.project.config") @@ -57,7 +57,7 @@ rule("wdk.man") import("core.base.option") import("core.theme.theme") import("core.project.depend") - import("private.utils.progress") + import("utils.progress") -- get ctrpp local ctrpp = target:data("wdk.ctrpp") diff --git a/xmake/rules/wdk/mc/xmake.lua b/xmake/rules/wdk/mc/xmake.lua index 32653dfc2..689c9786c 100644 --- a/xmake/rules/wdk/mc/xmake.lua +++ b/xmake/rules/wdk/mc/xmake.lua @@ -28,7 +28,7 @@ rule("wdk.mc") set_extensions(".mc") -- before load - before_load(function (target) + on_load(function (target) -- imports import("core.project.config") @@ -57,7 +57,7 @@ rule("wdk.mc") import("core.base.option") import("core.theme.theme") import("core.project.depend") - import("private.utils.progress") + import("utils.progress") -- get mc local mc = target:data("wdk.mc") diff --git a/xmake/rules/wdk/mof/xmake.lua b/xmake/rules/wdk/mof/xmake.lua index 319eec5d7..a1a2a0366 100644 --- a/xmake/rules/wdk/mof/xmake.lua +++ b/xmake/rules/wdk/mof/xmake.lua @@ -28,7 +28,7 @@ rule("wdk.mof") set_extensions(".mof") -- before load - before_load(function (target) + on_load(function (target) -- imports import("core.project.config") @@ -73,7 +73,7 @@ rule("wdk.mof") import("core.base.option") import("core.theme.theme") import("core.project.depend") - import("private.utils.progress") + import("utils.progress") -- get mofcomp local mofcomp = target:data("wdk.mofcomp") diff --git a/xmake/rules/wdk/sign/xmake.lua b/xmake/rules/wdk/sign/xmake.lua index 7dcc28785..6be72e7b3 100644 --- a/xmake/rules/wdk/sign/xmake.lua +++ b/xmake/rules/wdk/sign/xmake.lua @@ -34,7 +34,7 @@ rule("wdk.sign") add_deps("wdk.env") -- before load - before_load(function (target) + on_load(function (target) -- imports import("core.project.config") @@ -93,7 +93,7 @@ rule("wdk.sign") import("core.project.config") import("core.project.depend") import("lib.detect.find_file") - import("private.utils.progress") + import("utils.progress") -- need build this object? local tempfile = os.tmpfile(target:targetfile()) diff --git a/xmake/rules/wdk/tracewpp/xmake.lua b/xmake/rules/wdk/tracewpp/xmake.lua index 0c49bf5d0..75f4f37ef 100644 --- a/xmake/rules/wdk/tracewpp/xmake.lua +++ b/xmake/rules/wdk/tracewpp/xmake.lua @@ -25,7 +25,7 @@ rule("wdk.tracewpp") add_deps("wdk.env") -- before load - before_load(function (target) + on_load(function (target) -- imports import("core.project.config") @@ -57,7 +57,7 @@ rule("wdk.tracewpp") import("core.base.option") import("core.theme.theme") import("core.project.depend") - import("private.utils.progress") + import("utils.progress") -- get tracewpp local tracewpp = target:data("wdk.tracewpp") diff --git a/xmake/rules/winsdk/dotnet/xmake.lua b/xmake/rules/winsdk/dotnet/xmake.lua index 5274f040a..1cfef7b58 100644 --- a/xmake/rules/winsdk/dotnet/xmake.lua +++ b/xmake/rules/winsdk/dotnet/xmake.lua @@ -22,7 +22,7 @@ rule("win.sdk.dotnet") -- before load - before_load(function (target) + on_load(function (target) -- imports import("core.project.config") diff --git a/xmake/rules/winsdk/mfc/env/xmake.lua b/xmake/rules/winsdk/mfc/env/xmake.lua index f603ebfe2..f553618b5 100644 --- a/xmake/rules/winsdk/mfc/env/xmake.lua +++ b/xmake/rules/winsdk/mfc/env/xmake.lua @@ -22,5 +22,5 @@ rule("win.sdk.mfc.env") -- TODO: before load need check of vs's minverion, if defined - before_load(function (target) + on_load(function (target) end) diff --git a/xmake/rules/winsdk/xmake.lua b/xmake/rules/winsdk/xmake.lua index 0a3b105cc..297dfda75 100644 --- a/xmake/rules/winsdk/xmake.lua +++ b/xmake/rules/winsdk/xmake.lua @@ -27,7 +27,7 @@ rule("win.sdk.resource") rule("win.sdk.application") -- before load - before_load(function (target) + on_load(function (target) target:set("kind", "binary") end) diff --git a/xmake/rules/xcode/application/build.lua b/xmake/rules/xcode/application/build.lua index 786f5da89..a4862ae86 100644 --- a/xmake/rules/xcode/application/build.lua +++ b/xmake/rules/xcode/application/build.lua @@ -23,7 +23,7 @@ import("core.base.option") import("core.theme.theme") import("core.project.depend") import("private.tools.codesign") -import("private.utils.progress") +import("utils.progress") -- main entry function main (target, opt) diff --git a/xmake/rules/xcode/application/load.lua b/xmake/rules/xcode/application/load.lua index 3071ccd58..354aa5b0c 100644 --- a/xmake/rules/xcode/application/load.lua +++ b/xmake/rules/xcode/application/load.lua @@ -54,15 +54,4 @@ function main (target) -- register clean files for `xmake clean` target:add("cleanfiles", bundledir) - - -- depend xcode.framework? we need disable `build.across_targets_in_parallel` policy - local across_targets_in_parallel - for _, dep in ipairs(target:orderdeps()) do - if dep:rule("xcode.framework") then - across_targets_in_parallel = false - end - end - if across_targets_in_parallel ~= nil then - target:set("policy", "build.across_targets_in_parallel", across_targets_in_parallel) - end end diff --git a/xmake/rules/xcode/application/run.lua b/xmake/rules/xcode/application/run.lua index e92397a29..0e0663207 100644 --- a/xmake/rules/xcode/application/run.lua +++ b/xmake/rules/xcode/application/run.lua @@ -40,10 +40,10 @@ function _run_on_macosx(target, opt) -- add run environments local addrunenvs, setrunenvs = make_runenvs(target) for name, values in pairs(addrunenvs) do - os.addenv(name, unpack(table.wrap(values))) + os.addenv(name, table.unpack(table.wrap(values))) end for name, value in pairs(setrunenvs) do - os.setenv(name, unpack(table.wrap(value))) + os.setenv(name, table.unpack(table.wrap(value))) end -- debugging? diff --git a/xmake/rules/xcode/application/xmake.lua b/xmake/rules/xcode/application/xmake.lua index 5984bbb55..d328262c0 100644 --- a/xmake/rules/xcode/application/xmake.lua +++ b/xmake/rules/xcode/application/xmake.lua @@ -21,11 +21,24 @@ -- define rule: xcode application rule("xcode.application") - -- support add_files("Info.plist", "*.storyboard", "*.xcassets") - add_deps("xcode.info_plist", "xcode.storyboard", "xcode.xcassets") + -- support add_files("Info.plist", "*.storyboard", "*.xcassets", "*.metal") + add_deps("xcode.info_plist", "xcode.storyboard", "xcode.xcassets", "xcode.metal") -- we must set kind before target.on_load(), may we will use target in on_load() - before_load("load") + on_load("load") + + -- depend xcode.framework? we need disable `build.across_targets_in_parallel` policy + after_load(function (target) + local across_targets_in_parallel + for _, dep in ipairs(target:orderdeps()) do + if dep:rule("xcode.framework") then + across_targets_in_parallel = false + end + end + if across_targets_in_parallel ~= nil then + target:set("policy", "build.across_targets_in_parallel", across_targets_in_parallel) + end + end) -- build *.app after_build("build") diff --git a/xmake/rules/xcode/bundle/xmake.lua b/xmake/rules/xcode/bundle/xmake.lua index bd8e4329e..322a42418 100644 --- a/xmake/rules/xcode/bundle/xmake.lua +++ b/xmake/rules/xcode/bundle/xmake.lua @@ -25,7 +25,7 @@ rule("xcode.bundle") add_deps("xcode.info_plist") -- we must set kind before target.on_load(), may we will use target in on_load() - before_load(function (target) + on_load(function (target) -- get bundle directory local targetdir = target:targetdir() @@ -60,7 +60,7 @@ rule("xcode.bundle") import("core.theme.theme") import("core.project.depend") import("private.tools.codesign") - import("private.utils.progress") + import("utils.progress") -- get bundle and resources directory local bundledir = path.absolute(target:data("xcode.bundle.rootdir")) diff --git a/xmake/rules/xcode/framework/xmake.lua b/xmake/rules/xcode/framework/xmake.lua index b112dcaf1..de7d7c93c 100644 --- a/xmake/rules/xcode/framework/xmake.lua +++ b/xmake/rules/xcode/framework/xmake.lua @@ -25,7 +25,7 @@ rule("xcode.framework") add_deps("xcode.info_plist") -- we must set kind before target.on_load(), may we will use target in on_load() - before_load(function (target) + on_load(function (target) -- get framework directory local targetdir = target:targetdir() @@ -85,7 +85,7 @@ rule("xcode.framework") import("core.theme.theme") import("core.project.depend") import("private.tools.codesign") - import("private.utils.progress") + import("utils.progress") -- get framework directory local bundledir = path.absolute(target:data("xcode.bundle.rootdir")) diff --git a/xmake/rules/xcode/info_plist/xmake.lua b/xmake/rules/xcode/info_plist/xmake.lua index 57d3c9995..bd991b6e1 100644 --- a/xmake/rules/xcode/info_plist/xmake.lua +++ b/xmake/rules/xcode/info_plist/xmake.lua @@ -32,7 +32,7 @@ rule("xcode.info_plist") import("core.theme.theme") import("core.project.depend") import("core.tool.toolchain") - import("private.utils.progress") + import("utils.progress") -- check assert(path.filename(sourcefile) == "Info.plist", "we only support Info.plist file!") diff --git a/xmake/rules/xcode/metal/xmake.lua b/xmake/rules/xcode/metal/xmake.lua new file mode 100644 index 000000000..a876f946b --- /dev/null +++ b/xmake/rules/xcode/metal/xmake.lua @@ -0,0 +1,145 @@ +--!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 xmake.lua +-- + +-- build metal files +-- +-- @see https://developer.apple.com/documentation/metal/libraries/building_a_library_with_metal_s_command-line_tools +-- +rule("xcode.metal") + + -- support add_files("*.metal") + set_extensions(".metal") + + on_load(function (target) + local cross + if target:is_plat("macosx") then + cross = "xcrun -sdk macosx " + elseif target:is_plat("iphoneos") then + cross = target:is_arch("i386", "x86_64") and "xcrun -sdk iphonesimulator " or "xcrun -sdk iphoneos " + elseif target:is_plat("watchos") then + cross = target:is_arch("i386") and "xcrun -sdk watchsimulator " or "xcrun -sdk watchos " + elseif target:is_plat("appletvos") then + cross = target:is_arch("i386", "x86_64") and "xcrun -sdk appletvsimulator " or "xcrun -sdk appletvos " + else + raise("unknown platform for xcode!") + end + target:data_set("xcode.metal.cross", cross) + end) + + -- build *.metal to *.air + on_buildcmd_file(function (target, batchcmds, sourcefile, opt) + + -- get metal + import("core.tool.toolchain") + import("lib.detect.find_tool") + local cross = target:data("xcode.metal.cross") + local metal = assert(find_tool("metal", {program = cross .. " metal"}), "metal command not found!") + + -- get xcode toolchain + local xcode = toolchain.load("xcode", {plat = target:plat(), arch = target:arch()}) + local target_minver = xcode:config("target_minver") + local xcode_sysroot = xcode:config("xcode_sysroot") + + -- init metal arguments + local objectfile = target:objectfile(sourcefile) .. ".air" + local argv = {"-c", "-ffast-math", "-gline-tables-only"} + if target_minver then + table.insert(argv, "-target") + local airarch = target:is_arch("x86_64", "arm64") and "air64" or "air32" + if target:is_plat("macosx") then + table.insert(argv, airarch .. "-apple-macos" .. target_minver) + elseif target:is_plat("iphoneos") then + local airtarget = airarch .. "-apple-ios" .. target_minver + if target:is_arch("x86_64", "i386") then + airtarget = airtarget .. "-simulator" + end + table.insert(argv, airtarget) + elseif target:is_plat("watchos") then + local airtarget = airarch .. "-apple-watchos" .. target_minver + if target:is_arch("x86_64", "i386") then + airtarget = airtarget .. "-simulator" + end + table.insert(argv, airtarget) + elseif target:is_plat("appletvos") then + local airtarget = airarch .. "-apple-tvos" .. target_minver + if target:is_arch("x86_64", "i386") then + airtarget = airtarget .. "-simulator" + end + table.insert(argv, airtarget) + end + end + if xcode_sysroot then + table.insert(argv, "-isysroot") + table.insert(argv, xcode_sysroot) + end + table.insert(argv, "-o") + table.insert(argv, objectfile) + table.insert(argv, sourcefile) + + -- add commands + batchcmds:show_progress(opt.progress, "${color.build.object}compiling.metal %s", sourcefile) + batchcmds:mkdir(path.directory(objectfile)) + batchcmds:vrunv(metal.program, argv) + + -- add deps + batchcmds:add_depfiles(sourcefile) + batchcmds:set_depmtime(os.mtime(objectfile)) + batchcmds:set_depcache(target:dependfile(objectfile)) + end) + + -- link *.air to *.metallib + before_linkcmd(function (target, batchcmds, opt) + + -- get objectfiles + local objectfiles = {} + for rulename, sourcebatch in pairs(target:sourcebatches()) do + if rulename == "xcode.metal" then + for _, sourcefile in ipairs(sourcebatch.sourcefiles) do + table.insert(objectfiles, target:objectfile(sourcefile) .. ".air") + end + break + end + end + if #objectfiles == 0 then + return + end + + -- get metallib + import("core.tool.toolchain") + import("lib.detect.find_tool") + local cross = target:data("xcode.metal.cross") + local metallib = assert(find_tool("metallib", {program = cross .. " metallib"}), "metallib command not found!") + + -- get xcode toolchain + local xcode = toolchain.load("xcode", {plat = target:plat(), arch = target:arch()}) + local xcode_sysroot = xcode:config("xcode_sysroot") + + -- add commands + local resourcesdir = path.absolute(target:data("xcode.bundle.resourcesdir")) + local libraryfile = resourcesdir and path.join(resourcesdir, "default.metallib") or (target:targetfile() .. ".metallib") + batchcmds:show_progress(opt.progress, "${color.build.target}linking.metal %s", path.filename(libraryfile)) + batchcmds:mkdir(path.directory(libraryfile)) + batchcmds:vrunv(metallib.program, table.join({"-o", libraryfile}, objectfiles), {envs = {SDKROOT = xcode_sysroot}}) + + -- add deps + batchcmds:add_depfiles(objectfiles) + batchcmds:set_depmtime(os.mtime(libraryfile)) + batchcmds:set_depcache(target:dependfile(libraryfile)) + end) diff --git a/xmake/rules/xcode/storyboard/xmake.lua b/xmake/rules/xcode/storyboard/xmake.lua index 6ec23dd80..d96a1c002 100644 --- a/xmake/rules/xcode/storyboard/xmake.lua +++ b/xmake/rules/xcode/storyboard/xmake.lua @@ -32,7 +32,7 @@ rule("xcode.storyboard") import("core.theme.theme") import("core.project.depend") import("core.tool.toolchain") - import("private.utils.progress") + import("utils.progress") -- get xcode sdk directory local xcode_sdkdir = assert(get_config("xcode"), "xcode not found!") diff --git a/xmake/rules/xcode/xcassets/xmake.lua b/xmake/rules/xcode/xcassets/xmake.lua index 09185b226..9322d10c1 100644 --- a/xmake/rules/xcode/xcassets/xmake.lua +++ b/xmake/rules/xcode/xcassets/xmake.lua @@ -32,7 +32,7 @@ rule("xcode.xcassets") import("core.theme.theme") import("core.project.depend") import("core.tool.toolchain") - import("private.utils.progress") + import("utils.progress") -- get xcode sdk directory local xcode_sdkdir = assert(get_config("xcode"), "xcode not found!") diff --git a/xmake/rules/xmake_cli/xmake.lua b/xmake/rules/xmake_cli/xmake.lua index 2375c9e3b..a7e21a9e1 100644 --- a/xmake/rules/xmake_cli/xmake.lua +++ b/xmake/rules/xmake_cli/xmake.lua @@ -20,7 +20,7 @@ -- define rule: xmake cli program rule("xmake.cli") - before_load(function (target) + on_load(function (target) target:set("kind", "binary") assert(target:pkg("libxmake"), 'please add_packages("libxmake") to target(%s) first!', target:name()) end) |
