diff options
| author | ruki <[email protected]> | 2025-12-03 06:15:57 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-12-03 06:15:57 +0800 |
| commit | 605ce1f6bcfa8bcbe1a1f8151361f2539fb09727 (patch) | |
| tree | 8791253d3233c9ec39b0ff6a302e51406a84a789 | |
| parent | 6e84afe485a93d52c1971cf8d6287679057e76dc (diff) | |
| parent | add6b5d52685fb9833a906caf0e14cb9238a1c4e (diff) | |
Merge pull request #7094 from xmake-io/syntax
Add `xmake check syntax` support
| -rw-r--r-- | xmake/actions/build/build_files.lua | 9 | ||||
| -rw-r--r-- | xmake/core/project/project.lua | 18 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/import/core/project/project.lua | 1 | ||||
| -rw-r--r-- | xmake/modules/core/tools/cl.lua | 14 | ||||
| -rw-r--r-- | xmake/modules/core/tools/gcc.lua | 12 | ||||
| -rw-r--r-- | xmake/modules/private/action/build/build_binary.lua | 20 | ||||
| -rw-r--r-- | xmake/modules/private/action/build/target.lua | 4 | ||||
| -rw-r--r-- | xmake/modules/private/check/checker.lua | 4 | ||||
| -rw-r--r-- | xmake/modules/private/check/checkers/clang/tidy.lua | 16 | ||||
| -rw-r--r-- | xmake/modules/private/check/checkers/syntax.lua | 183 | ||||
| -rw-r--r-- | xmake/rules/c++/config/main.lua | 2 |
11 files changed, 272 insertions, 11 deletions
diff --git a/xmake/actions/build/build_files.lua b/xmake/actions/build/build_files.lua index 6818418f2..26b389ba6 100644 --- a/xmake/actions/build/build_files.lua +++ b/xmake/actions/build/build_files.lua @@ -30,6 +30,9 @@ import("deprecated.build_files", {alias = "deprecated_build_files"}) -- convert all sourcefiles to lua pattern function _get_file_patterns(sourcefiles) local patterns = {} + if not sourcefiles then + return patterns + end for _, sourcefile in ipairs(path.splitenv(sourcefiles)) do -- get the excludes @@ -90,7 +93,11 @@ function _build_files(targets_root, opt) opt.distcc = distcc_build_client.singleton() end if not target_buildutils.run_filejobs(targets_root, opt) then - wprint("%s not found!", opt.sourcefiles) + if opt.sourcefiles then + wprint("%s not found!", opt.sourcefiles) + else + wprint("no files found!") + end end end diff --git a/xmake/core/project/project.lua b/xmake/core/project/project.lua index f10cdc894..43b825583 100644 --- a/xmake/core/project/project.lua +++ b/xmake/core/project/project.lua @@ -964,6 +964,24 @@ function project.policy(name) return policy.check(name, policies and policies[name]) end +-- set the project policy in memory +function project.policy_set(name, value) + -- get current policies from cache or initialize + local policies = project._memcache():get("policies") + if not policies then + -- force initialization by calling policy() once + project.policy(name) + policies = project._memcache():get("policies") + end + policies = policies or {} + + -- set the policy value + policies[name] = value + + -- update cache + project._memcache():set("policies", policies) +end + -- project has been loaded? function project.is_loaded() return project._memcache():get("targets_loaded") diff --git a/xmake/core/sandbox/modules/import/core/project/project.lua b/xmake/core/sandbox/modules/import/core/project/project.lua index 4a191576c..8a808e7da 100644 --- a/xmake/core/sandbox/modules/import/core/project/project.lua +++ b/xmake/core/sandbox/modules/import/core/project/project.lua @@ -67,6 +67,7 @@ sandbox_core_project.requireconfs_str = project.requireconfs_str sandbox_core_project.requireslock = project.requireslock sandbox_core_project.requireslock_version = project.requireslock_version sandbox_core_project.policy = project.policy +sandbox_core_project.policy_set = project.policy_set sandbox_core_project.tmpdir = project.tmpdir sandbox_core_project.tmpfile = project.tmpfile sandbox_core_project.is_loaded = project.is_loaded diff --git a/xmake/modules/core/tools/cl.lua b/xmake/modules/core/tools/cl.lua index de64df3da..46ecc9b2b 100644 --- a/xmake/modules/core/tools/cl.lua +++ b/xmake/modules/core/tools/cl.lua @@ -22,6 +22,7 @@ import("core.base.option") import("core.base.global") import("core.base.hashset") +import("core.cache.memcache") import("core.project.project") import("core.project.policy") import("core.language.language") @@ -93,6 +94,11 @@ function init(self) }) end +-- is syntax check enabled? +function _is_syntax_check() + return memcache.get("syntax_check", "enabled") or false +end + -- make the symbol flags function nf_symbols(self, levels, opt) local flags = nil @@ -654,6 +660,7 @@ end -- make the compile arguments list function compargv(self, sourcefile, objectfile, flags, opt) + opt = opt or {} -- precompiled header? local extension = path.extension(sourcefile) @@ -661,6 +668,13 @@ function compargv(self, sourcefile, objectfile, flags, opt) return _compargv_pch(self, sourcefile, objectfile, flags) end + -- if syntax-only, add /Zs and skip -c and -Fo + if _is_syntax_check() then + table.insert(flags, "/Zs") + local argv = table.join(flags, sourcefile) + return self:program(), (opt and opt.rawargs) and argv or winos.cmdargv(argv) + end + -- suppress clang-cl warnings -- clang-cl: warning: argument unused during compilation: '-c' [-Wunused-command-line-argument] -- diff --git a/xmake/modules/core/tools/gcc.lua b/xmake/modules/core/tools/gcc.lua index 4a920a233..c1a98b752 100644 --- a/xmake/modules/core/tools/gcc.lua +++ b/xmake/modules/core/tools/gcc.lua @@ -73,6 +73,11 @@ function _is_cosmocc(self) return is_cosmocc end +-- is syntax check enabled? +function _is_syntax_check() + return memcache.get("syntax_check", "enabled") or false +end + -- get `-MMD -MF depfile.d` flags, some old gcc does not support it at same time function _get_depfile_flags(self) local depfile_flags = _g._DEPFILE_FLAGS @@ -965,6 +970,7 @@ end -- make the compile arguments list function compargv(self, sourcefile, objectfile, flags, opt) + opt = opt or {} -- is precompiled header or module files? remove the force includes. local extension = path.extension(sourcefile) @@ -974,6 +980,12 @@ function compargv(self, sourcefile, objectfile, flags, opt) flags = _translate_flags_for_mpp(self, flags) end + -- if syntax-only, add -fsyntax-only and skip -c and -o + if _is_syntax_check() then + table.insert(flags, "-fsyntax-only") + return self:program(), table.join(flags, sourcefile) + end + local argv = table.join("-c", flags, "-o", objectfile, sourcefile) return self:program(), argv end diff --git a/xmake/modules/private/action/build/build_binary.lua b/xmake/modules/private/action/build/build_binary.lua index 581b2dde9..47884fd44 100644 --- a/xmake/modules/private/action/build/build_binary.lua +++ b/xmake/modules/private/action/build/build_binary.lua @@ -23,6 +23,7 @@ import("build_object") import("private.action.build.target", {alias = "target_buildutils"}) function main(jobgraph, target, opt) + opt = opt or {} local objects_group = target:fullname() .. "/objects" local jobsize = jobgraph:size() jobgraph:group(objects_group, function () @@ -30,13 +31,16 @@ function main(jobgraph, target, opt) end) local has_object_jobs = jobgraph:size() > jobsize - -- @note We always need the link task, even if the current target does not have any object files - -- https://github.com/xmake-io/xmake-repo/pull/7479#issuecomment-3007049158 - local link_group = target:fullname() .. "/link" - jobgraph:group(link_group, function () - target_buildutils.add_linkjobs(jobgraph, target, opt) - end) - if has_object_jobs then - jobgraph:add_orders(objects_group, link_group) + -- skip link jobs if linkjobs is disabled + if opt.linkjobs ~= false then + -- @note We always need the link task, even if the current target does not have any object files + -- https://github.com/xmake-io/xmake-repo/pull/7479#issuecomment-3007049158 + local link_group = target:fullname() .. "/link" + jobgraph:group(link_group, function () + target_buildutils.add_linkjobs(jobgraph, target, opt) + end) + if has_object_jobs then + jobgraph:add_orders(objects_group, link_group) + end end end diff --git a/xmake/modules/private/action/build/target.lua b/xmake/modules/private/action/build/target.lua index 280610e71..e906d4ee8 100644 --- a/xmake/modules/private/action/build/target.lua +++ b/xmake/modules/private/action/build/target.lua @@ -701,6 +701,10 @@ end -- add link jobs for the given target function add_linkjobs(jobgraph, target, opt) opt = table.clone(opt or {}) + -- skip link jobs if linkjobs is disabled + if opt.linkjobs == false then + return + end opt.job_kind = "link" local with_stages = opt.with_stages local group, group_before, group_after diff --git a/xmake/modules/private/check/checker.lua b/xmake/modules/private/check/checker.lua index 6ff8b3864..6d43eecb9 100644 --- a/xmake/modules/private/check/checker.lua +++ b/xmake/modules/private/check/checker.lua @@ -59,7 +59,9 @@ function checkers() -- cuda checkers ["cuda.devlink"] = {description = "Check devlink for targets.", build_failure = true}, -- clang tidy checker - ["clang.tidy"] = {description = "Check project code using clang-tidy.", showstats = false} + ["clang.tidy"] = {description = "Check project code using clang-tidy.", showstats = false}, + -- syntax checker + ["syntax"] = {description = "Check the project sourcecode syntax without linking.", showstats = false} } _g._CHECKERS = checkers end diff --git a/xmake/modules/private/check/checkers/clang/tidy.lua b/xmake/modules/private/check/checkers/clang/tidy.lua index 82e9246fb..a77b3e946 100644 --- a/xmake/modules/private/check/checkers/clang/tidy.lua +++ b/xmake/modules/private/check/checkers/clang/tidy.lua @@ -36,6 +36,8 @@ local options = { {"j", "jobs", "kv", tostring(os.default_njob()), "Set the number of parallel check jobs."}, {"q", "quiet", "k", nil, "Run clang-tidy in quiet mode."}, + {"v", "verbose", "k", nil, "Print lots of verbose information for users."}, + {"D", "diagnosis", "k", nil, "Print lots of diagnosis information (backtrace, check info ..) only for developers."}, {nil, "fix", "k", nil, "Apply suggested fixes."}, {nil, "fix_errors", "k", nil, "Apply suggested errors fixes."}, {nil, "fix_notes", "k", nil, "Apply suggested notes fixes."}, @@ -242,6 +244,17 @@ function main(argv) , "" , "Usage: xmake check clang.tidy [options]") + -- save option context + option.save() + + -- set verbose and diagnosis if specified + if args.verbose then + option.set("verbose", true) + end + if args.diagnosis then + option.set("diagnosis", true) + end + -- enter the environments of llvm local oldenvs = packagenv.enter("llvm") @@ -272,4 +285,7 @@ function main(argv) _check(clang_tidy, args) end os.setenvs(oldenvs) + + -- restore option context + option.restore() end diff --git a/xmake/modules/private/check/checkers/syntax.lua b/xmake/modules/private/check/checkers/syntax.lua new file mode 100644 index 000000000..7da098871 --- /dev/null +++ b/xmake/modules/private/check/checkers/syntax.lua @@ -0,0 +1,183 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015-present, Xmake Open Source Community. +-- +-- @author ruki +-- @file syntax.lua +-- + +-- imports +import("core.base.option") +import("core.base.task") +import("core.cache.memcache") +import("core.project.project") +import("actions.build.build_files", {rootdir = os.programdir(), alias = "build_files"}) +import("actions.build.build", {rootdir = os.programdir(), alias = "build"}) +import("utils.progress") + +-- the syntax check options +local options = { + {'f', "files", "kv", nil, "Check the given source files.", + "e.g.", + " - xmake check syntax -f src/foo.cpp", + " - xmake check syntax -f 'src/*.cpp'"}, + {"j", "jobs", "kv", tostring(os.default_njob()), + "Set the number of parallel check jobs."}, + {"v", "verbose", "k", nil, "Print lots of verbose information for users."}, + {"D", "diagnosis", "k", nil, "Print lots of diagnosis information (backtrace, check info ..) only for developers."}, + {nil, "targets", "vs", nil, "Check the sourcefiles of the given target.", + "e.g.", + " - xmake check syntax", + " - xmake check syntax [targets]"} +} + +-- check if target has C++ rules +function _has_cpp_rules(target) + for _, ruleinst in ipairs(target:orderules()) do + local rulename = ruleinst:name() + if rulename == "c++.build" or rulename == "c.build" or + rulename == "objc++.build" or rulename == "objc.build" then + return true + end + end + return false +end + +-- check if compiler supports syntax-only check +function _check_compiler_support(target) + local has_support = false + if target:has_tool("cc", "gcc", "clang") or target:has_tool("cxx", "gxx", "clangxx") then + -- gcc/clang: -fsyntax-only + has_support = true + elseif target:has_tool("cc", "cl") or target:has_tool("cxx", "cl") then + -- MSVC: /Zs + has_support = true + end + return has_support +end + +-- validate targets and enable syntax-only +function _validate_and_enable_targets(opt) + opt = opt or {} + local targets = {} + if opt.targets then + for _, targetname in ipairs(opt.targets) do + local target = project.target(targetname) + if target then + table.insert(targets, target) + end + end + else + for _, target in pairs(project.targets()) do + if target:is_enabled() and (target:is_default() or option.get("all")) then + table.insert(targets, target) + end + end + end + + -- check if any target has C++ rules and enable syntax-only + local cpp_targets = {} + for _, target in ipairs(targets) do + if _has_cpp_rules(target) then + -- check if compiler supports syntax-only check + if not _check_compiler_support(target) then + wprint("target(%s): current compiler does not support syntax-only check", target:name()) + else + table.insert(cpp_targets, target) + end + else + wprint("target(%s): syntax check currently only supports C/C++ targets", target:name()) + end + end + + -- enable syntax-only via memcache + if #cpp_targets > 0 then + memcache.set("syntax_check", "enabled", true) + end + + -- if no C++ target found, return false to skip checking + if #cpp_targets == 0 then + return false + end + return true +end + +-- do check +function _check(opt) + opt = opt or {} + + local sourcefiles = opt.files + local targetnames = opt.targets + local jobs = opt.jobs and tonumber(opt.jobs) or nil + + local check_time = os.mclock() + local build_opt = {linkjobs = false} + if jobs then + build_opt.jobs = jobs + end + if sourcefiles then + build_opt.sourcefiles = sourcefiles + build_files(targetnames, build_opt) + else + build(targetnames, build_opt) + end + check_time = os.mclock() - check_time + progress.show(100, "${color.success}syntax check ok, spent %.3fs", check_time / 1000) +end + +function main(argv) + -- parse arguments + local args = option.parse(argv or {}, options, "Check the project sourcecode syntax without linking." + , "" + , "Usage: xmake check syntax [options]") + + -- save option context + option.save() + + -- set verbose and diagnosis if specified + if args.verbose then + option.set("verbose", true) + end + if args.diagnosis then + option.set("diagnosis", true) + end + + -- lock the whole project + project.lock() + + -- disable ccache after config + project.policy_set("build.ccache", false) + + -- enter project directory + local oldir = os.cd(project.directory()) + + -- it will call on_config to add some missing flags in rules + project.load_targets() + + -- validate targets and enable syntax-only + if _validate_and_enable_targets(args) then + _check(args) + end + + -- leave project directory + os.cd(oldir) + + -- unlock the whole project + project.unlock() + + -- restore option context + option.restore() +end + diff --git a/xmake/rules/c++/config/main.lua b/xmake/rules/c++/config/main.lua index dce7cff6b..68bf5a064 100644 --- a/xmake/rules/c++/config/main.lua +++ b/xmake/rules/c++/config/main.lua @@ -20,9 +20,9 @@ -- imports import("rules.c++.config.basic", {rootdir = os.programdir(), alias = "config_basic"}) +import("rules.c++.config.dynamic_debugging", {rootdir = os.programdir(), alias = "config_dynamic_debugging"}) import("rules.c++.config.optimization", {rootdir = os.programdir(), alias = "config_optimization"}) import("rules.c++.config.sanitizer", {rootdir = os.programdir(), alias = "config_sanitizer"}) -import("rules.c++.config.dynamic_debugging", {rootdir = os.programdir(), alias = "config_dynamic_debugging"}) -- main entry function main(target, sourcekind) |
