diff options
| author | ruki <[email protected]> | 2023-03-10 23:08:54 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-03-10 23:08:54 +0800 |
| commit | 744ba2c9444fe7b7364314530db5aa6aa3c43d81 (patch) | |
| tree | 34bfdc97096f1b349bc00e822b0a611639614b20 | |
| parent | fdb8bc00233b61f17390f2ac60ae18d710a7a535 (diff) | |
| parent | 816c06fa10048cd8be85b7160838d21d59644e41 (diff) | |
Merge pull request #3494 from xmake-io/makefile
improve makefile generator
| -rw-r--r-- | xmake/plugins/project/make/makefile.lua | 440 | ||||
| -rw-r--r-- | xmake/plugins/project/ninja/build_ninja.lua | 1 |
2 files changed, 247 insertions, 194 deletions
diff --git a/xmake/plugins/project/make/makefile.lua b/xmake/plugins/project/make/makefile.lua index 87c3b6f55..ef1122857 100644 --- a/xmake/plugins/project/make/makefile.lua +++ b/xmake/plugins/project/make/makefile.lua @@ -26,44 +26,57 @@ import("core.language.language") import("core.platform.platform") import("lib.detect.find_tool") --- get log makefile -function _logfile() - return vformat("$(buildir)/.build.log") -end - --- mkdir directory -function _mkdir(makefile, dir) - if is_subhost("windows") then - makefile:print("\t-@mkdir %s > NUL 2>&1", dir) +-- tranlate path +function _translate_path(filepath, outputdir) + filepath = path.translate(filepath) + if filepath == "" then + return "" + end + if path.is_absolute(filepath) then + if filepath:startswith(project.directory()) then + return path.relative(filepath, outputdir) + end + return filepath else - makefile:print("\t@mkdir -p %s", dir) + return path.relative(path.absolute(filepath), outputdir) end end --- copy file -function _cp(makefile, sourcefile, targetfile) - if is_subhost("windows") then - makefile:print("\t@copy /Y %s %s > NUL 2>&1", sourcefile, targetfile) - else - makefile:print("\t@cp %s %s", sourcefile, targetfile) - end +-- get relative unix path +function _get_relative_unix_path(filepath, outputdir) + filepath = _translate_path(filepath, outputdir) + filepath = path.translate(filepath) + return os.args(filepath) end --- try to remove the given file or directory -function _tryrm(makefile, filedir) - if is_subhost("windows") then - -- we attempt to delete it as file first, we remove it as directory if failed - makefile:print("\t@del /F /Q %s > NUL 2>&1 || rmdir /S /Q %s > NUL 2>&1", filedir, filedir) - else - makefile:print("\t@rm -rf %s", filedir) +-- translate compiler flags +function _translate_compflags(compflags, outputdir) + local flags = {} + for _, flag in ipairs(compflags) do + for _, pattern in ipairs({"[%-](I)(.*)", "[%-](isystem)(.*)"}) do + flag = flag:gsub(pattern, function (flag, dir) + dir = _get_relative_unix_path(dir, outputdir) + return "-" .. flag .. dir + end) + end + table.insert(flags, flag) end + return flags end --- remove the given files or directories -function _remove(makefile, filedirs) - for _, filedir in ipairs(filedirs) do - _tryrm(makefile, filedir) +-- translate linker flags +function _translate_linkflags(linkflags, outputdir) + local flags = {} + for _, flag in ipairs(linkflags) do + for _, pattern in ipairs({"[%-](L)(.*)", "[%-](F)(.*)"}) do + flag = flag:gsub(pattern, function (flag, dir) + dir = _get_relative_unix_path(dir, outputdir) + return "-" .. flag .. dir + end) + end + table.insert(flags, flag) end + return flags end -- get program from target toolchains @@ -78,8 +91,8 @@ function _get_program_from_target(target, toolkind) return program end --- make common flags -function _make_common_flags(target, sourcekind, sourcebatch) +-- get common flags +function _get_common_flags(target, sourcekind, sourcebatch) -- make source flags local sourceflags = {} @@ -125,13 +138,140 @@ function _make_common_flags(target, sourcekind, sourcebatch) end sourceflags_[sourcefile] = otherflags end - - -- ok? return commonflags, sourceflags_ end --- make the object -function _make_object(makefile, target, sourcefile, objectfile, sourceflags) + +-- mkdir directory +function _add_create_directory(makefile, dir) + if is_subhost("windows") then + makefile:print("\t-@mkdir %s > NUL 2>&1", dir) + else + makefile:print("\t@mkdir -p %s", dir) + end +end + +-- copy file +function _add_copy_file(makefile, sourcefile, targetfile) + if is_subhost("windows") then + makefile:print("\t@copy /Y %s %s > NUL 2>&1", sourcefile, targetfile) + else + makefile:print("\t@cp %s %s", sourcefile, targetfile) + end +end + +-- try to remove the given file or directory +function _add_remove_file(makefile, filedir) + if is_subhost("windows") then + -- we attempt to delete it as file first, we remove it as directory if failed + makefile:print("\t@del /F /Q %s > NUL 2>&1 || rmdir /S /Q %s > NUL 2>&1", filedir, filedir) + else + makefile:print("\t@rm -rf %s", filedir) + end +end + +-- remove the given files or directories +function _add_remove_files(makefile, filedirs, outputdir) + for _, filedir in ipairs(filedirs) do + filedir = _get_relative_unix_path(filedir, outputdir) + _add_remove_file(makefile, filedir, outputdir) + end +end + +-- add header +function _add_header(makefile) + makefile:print([[# this is the build file for project %s +# it is autogenerated by the xmake build system. +# do not edit by hand. +]], project.name() or "") +end + +-- add switches +function _add_switches(makefile) + if is_subhost("windows") then + makefile:print("!if \"%$(VERBOSE)\" != \"1\"") + makefile:print("VV=@") + makefile:print("!endif") + else + makefile:print("ifneq (%$(VERBOSE),1)") + makefile:print("VV=@") + makefile:print("endif") + end + makefile:print("") +end + +-- add toolchains +function _add_toolchains(makefile, outputdir) + + -- add ccache + local ccache = find_tool("ccache") + if ccache then + makefile:print("CCACHE=" .. ccache.program) + end + + -- add compilers + for sourcekind, _ in pairs(language.sourcekinds()) do + local program = platform.tool(sourcekind) + if program and program ~= "" then + makefile:print("%s=%s", sourcekind:upper(), program) + end + end + makefile:print("") + + -- add linkers + local linkerkinds = {} + for _, _linkerkinds in pairs(language.targetkinds()) do + table.join2(linkerkinds, _linkerkinds) + end + for _, linkerkind in ipairs(table.unique(linkerkinds)) do + local program = platform.tool(linkerkind) + if program and program ~= "" then + makefile:print("%s=%s", (linkerkind:upper():gsub('%-', '_')), program) + end + end + makefile:print("") + + -- add toolchains from targets + for targetname, target in pairs(project.targets()) do + if not target:is_phony() then + local program = _get_program_from_target(target, target:linker():kind()) + if program then + makefile:print("%s_%s=%s", targetname, target:linker():kind():upper(), program) + end + for _, sourcebatch in pairs(target:sourcebatches()) do + local sourcekind = sourcebatch.sourcekind + if sourcekind then + local program = _get_program_from_target(target, sourcekind) + if program then + makefile:print("%s_%s=%s", targetname, sourcekind:upper(), program) + end + end + end + end + end + makefile:print("") +end + +-- add flags +function _add_flags(makefile, targetflags, outputdir) + for targetname, target in pairs(project.targets()) do + if not target:is_phony() then + for _, sourcebatch in pairs(target:sourcebatches()) do + local sourcekind = sourcebatch.sourcekind + if sourcekind then + local commonflags, sourceflags = _get_common_flags(target, sourcekind, sourcebatch) + makefile:print("%s_%sFLAGS=%s", targetname, sourcekind:upper(), os.args(_translate_compflags(commonflags, outputdir))) + targetflags[targetname .. '_' .. sourcekind:upper()] = sourceflags + end + end + makefile:print("%s_%sFLAGS=%s", targetname, target:linker():kind():upper(), os.args(_translate_linkflags(target:linkflags(), outputdir))) + end + end + makefile:print("") +end + +-- add build object +function _add_build_object(makefile, target, sourcefile, objectfile, sourceflags, outputdir) -- get the source file kind local sourcekind = language.sourcekind_of(sourcefile) @@ -145,7 +285,11 @@ function _make_object(makefile, target, sourcefile, objectfile, sourceflags) end -- get complier flags - local compflags = sourceflags[sourcefile] + local compflags = _translate_compflags(sourceflags[sourcefile], outputdir) + + -- translate file paths + sourcefile = _get_relative_unix_path(sourcefile, outputdir) + objectfile = _get_relative_unix_path(objectfile, outputdir) -- make command local macro = "$\01" .. target:name() .. '_' .. sourcekind:upper() .. "FLAGS\02" @@ -183,15 +327,15 @@ function _make_object(makefile, target, sourcefile, objectfile, sourceflags) -- make body makefile:print("\t@echo %scompiling.$(mode) %s", ccache and "ccache " or "", sourcefile) - _mkdir(makefile, path.directory(objectfile)) - makefile:writef("\t@%s > %s 2>&1\n", command, _logfile()) + _add_create_directory(makefile, path.directory(objectfile)) + makefile:writef("\t$(VV)%s\n", command) -- make tail makefile:print("") end --- make objects -function _make_objects(makefile, target, sourcekind, sourcebatch, sourceflags) +-- add build objects +function _add_build_objects(makefile, target, sourcekind, sourcebatch, sourceflags, outputdir) local handled_objects = target:data("makefile.handled_objects") if not handled_objects then handled_objects = {} @@ -201,14 +345,14 @@ function _make_objects(makefile, target, sourcekind, sourcebatch, sourceflags) -- remove repeat -- this is because some rules will repeatedly bind the same sourcekind, e.g. `rule("c++.build.modules.builder")` if not handled_objects[objectfile] then - _make_object(makefile, target, sourcebatch.sourcefiles[index], objectfile, sourceflags) + _add_build_object(makefile, target, sourcebatch.sourcefiles[index], objectfile, sourceflags, outputdir) handled_objects[objectfile] = true end end end --- make phony -function _make_phony(makefile, target) +-- add build phony +function _add_build_phony(makefile, target) -- make dependence for the dependent targets makefile:printf("%s:", target:name()) @@ -218,25 +362,25 @@ function _make_phony(makefile, target) makefile:print("") end --- make target -function _make_target(makefile, target, targetflags) +-- add build target +function _add_build_target(makefile, target, targetflags, outputdir) -- https://github.com/xmake-io/xmake/issues/2337 target:data_set("plugin.project.kind", "makefile") -- is phony target? if target:is_phony() then - return _make_phony(makefile, target) + return _add_build_phony(makefile, target) end -- make head - local targetfile = target:targetfile() + local targetfile = _get_relative_unix_path(target:targetfile(), outputdir) local targetname = target:name() -- rules like `./target` and `target` are equivalent and can causes issues -- for cases where targetdir is . -- in these cases, the targetfile rule is not created - if targetfile == "./" .. targetname then + if target:targetfile() == "./" .. targetname then makefile:printf("%s:", targetname) else makefile:print("%s: %s", targetname, targetfile) @@ -246,12 +390,15 @@ function _make_target(makefile, target, targetflags) -- make dependence for the dependent targets for _, depname in ipairs(target:get("deps")) do local dep = project.target(depname) - makefile:write(" " .. (dep:is_phony() and depname or dep:targetfile())) + makefile:write(" " .. (dep:is_phony() and depname or _get_relative_unix_path(dep:targetfile(), outputdir))) end -- make dependence for objects local objectfiles = target:objectfiles() + local objectfiles_translated = {} for _, objectfile in ipairs(objectfiles) do + objectfile = _get_relative_unix_path(objectfile, outputdir) + table.insert(objectfiles_translated, objectfile) makefile:write(" " .. objectfile) end @@ -270,7 +417,7 @@ function _make_target(makefile, target, targetflags) end -- get command - local command = target:linkcmd() + local command = target:linker():linkcmd(objectfiles_translated, targetfile, {target = target}) -- replace linkflags to $(XX) local p, e = command:find(os.args(target:linkflags()), 1, true) @@ -293,32 +440,8 @@ function _make_target(makefile, target, targetflags) -- make body makefile:print("\t@echo linking.$(mode) %s", path.filename(targetfile)) - _mkdir(makefile, path.directory(targetfile)) - makefile:writef("\t@%s > %s 2>&1\n", command, _logfile()) - - -- TODO make header directories (deprecated) - local dstheaderdirs = {} - local srcheaders, dstheaders = target:headers() - for _, dstheader in ipairs(dstheaders) do - dstheaderdirs[path.directory(dstheader)] = true - end - for dstheaderdir, _ in pairs(dstheaderdirs) do - _mkdir(makefile, dstheaderdir) - end - - -- copy headers - if srcheaders and dstheaders then - local i = 1 - for _, srcheader in ipairs(srcheaders) do - local dstheader = dstheaders[i] - if dstheader then - _cp(makefile, srcheader, dstheader) - end - i = i + 1 - end - end - - -- make tail + _add_create_directory(makefile, path.directory(targetfile)) + makefile:writef("\t$(VV)%s\n", command) makefile:print("") -- build source batches @@ -327,89 +450,13 @@ function _make_target(makefile, target, targetflags) if sourcekind then -- compile source files to single object at once local sourceflags = targetflags[target:name() .. '_' .. sourcekind:upper()] - _make_objects(makefile, target, sourcekind, sourcebatch, sourceflags) + _add_build_objects(makefile, target, sourcekind, sourcebatch, sourceflags, outputdir) end end end --- make all -function _make_all(makefile) - - -- make head - makefile:print([[# this is the build file for project %s -# it is autogenerated by the xmake build system. -# do not edit by hand. -]], project.name() or "") - - -- make variables for ccache - local ccache = find_tool("ccache") - if ccache then - makefile:print("CCACHE=" .. ccache.program) - end - - -- make variables for source kinds - for sourcekind, _ in pairs(language.sourcekinds()) do - local program = platform.tool(sourcekind) - if program and program ~= "" then - makefile:print("%s=%s", sourcekind:upper(), program) - end - end - makefile:print("") - - -- make variables for linker kinds - local linkerkinds = {} - for _, _linkerkinds in pairs(language.targetkinds()) do - table.join2(linkerkinds, _linkerkinds) - end - for _, linkerkind in ipairs(table.unique(linkerkinds)) do - local program = platform.tool(linkerkind) - if program and program ~= "" then - makefile:print("%s=%s", (linkerkind:upper():gsub('%-', '_')), program) - end - end - makefile:print("") - - -- TODO - -- disable precompiled header first - for _, target in pairs(project.targets()) do - target:set("pcheader", nil) - target:set("pcxxheader", nil) - end - - -- make variables for target - local targetflags = {} - for targetname, target in pairs(project.targets()) do - if not target:is_phony() then - - -- make target linker - local program = _get_program_from_target(target, target:linker():kind()) - if program then - makefile:print("%s_%s=%s", targetname, target:linker():kind():upper(), program) - end - - -- make target flags - for _, sourcebatch in pairs(target:sourcebatches()) do - local sourcekind = sourcebatch.sourcekind - if sourcekind then - - -- make source compiler - local program = _get_program_from_target(target, sourcekind) - if program then - makefile:print("%s_%s=%s", targetname, sourcekind:upper(), program) - end - - -- make source flags - local commonflags, sourceflags = _make_common_flags(target, sourcekind, sourcebatch) - makefile:print("%s_%sFLAGS=%s", targetname, sourcekind:upper(), os.args(commonflags)) - targetflags[targetname .. '_' .. sourcekind:upper()] = sourceflags - end - end - makefile:print("%s_%sFLAGS=%s", targetname, target:linker():kind():upper(), os.args(target:linkflags())) - end - end - makefile:print("") - - -- make all +-- add build targets +function _add_build_targets(makefile, targetflags, outputdir) local default = "" for targetname, target in pairs(project.targets()) do if target:is_default() then @@ -423,81 +470,88 @@ function _make_all(makefile) end makefile:print("all: %s\n", all) makefile:print(".PHONY: default all %s\n", all) - - -- make it for all targets for _, target in pairs(project.targets()) do - _make_target(makefile, target, targetflags) + _add_build_target(makefile, target, targetflags, outputdir) end end --- clean target -function _clean_target(makefile, target) +-- add build +function _add_build(makefile, targetflags, outputdir) - -- make head - makefile:printf("clean_%s: ", target:name()) + -- TODO + -- disable precompiled header first + for _, target in pairs(project.targets()) do + target:set("pcheader", nil) + target:set("pcxxheader", nil) + end - -- make dependence for the dependent targets + -- add build targets + _add_build_targets(makefile, targetflags, outputdir) +end + +-- add clean target +function _add_clean_target(makefile, target, outputdir) + makefile:printf("clean_%s: ", target:name()) for _, dep in ipairs(target:get("deps")) do makefile:write(" clean_" .. dep) end - - -- make dependence end makefile:print("") - - -- make body if not target:is_phony() then - - -- remove the target file - _remove(makefile, target:targetfile()) - - -- remove the symbol file - _remove(makefile, target:symbolfile()) - - -- remove the object files - _remove(makefile, target:objectfiles()) - + _add_remove_files(makefile, target:targetfile(), outputdir) + _add_remove_files(makefile, target:symbolfile(), outputdir) + _add_remove_files(makefile, target:objectfiles(), outputdir) -- TODO remove the header files (deprecated) local _, dstheaders = target:headers() - _remove(makefile, dstheaders) + _add_remove_files(makefile, dstheaders, outputdir) end - - -- make tail makefile:print("") end --- clean all -function _clean_all(makefile) - - -- clean all +-- add clean targets +function _add_clean_targets(makefile, outputdir) local all = "" for targetname, _ in pairs(project.targets()) do all = all .. " clean_" .. targetname end makefile:print("clean: %s\n", all) - -- clean targets + -- add clean targets for _, target in pairs(project.targets()) do - _clean_target(makefile, target) + _add_clean_target(makefile, target, outputdir) end end --- make +-- add clean +function _add_clean(makefile, outputdir) + _add_clean_targets(makefile, outputdir) +end + function make(outputdir) -- enter project directory local oldir = os.cd(os.projectdir()) - -- remove the log makefile first - os.rm(_logfile()) - -- open the makefile local makefile = io.open(path.join(outputdir, "makefile"), "w") - -- make all - _make_all(makefile) + -- add header + _add_header(makefile) + + -- add switches + _add_switches(makefile) + + -- add toolchains + _add_toolchains(makefile, outputdir) + + -- add flags + local targetflags = {} + _add_flags(makefile, targetflags, outputdir) + + -- add build + _add_build(makefile, targetflags, outputdir) - -- clean all - _clean_all(makefile) + -- add clean + _add_clean(makefile, outputdir) -- close the makefile makefile:close() diff --git a/xmake/plugins/project/ninja/build_ninja.lua b/xmake/plugins/project/ninja/build_ninja.lua index a97aac804..cb757d484 100644 --- a/xmake/plugins/project/ninja/build_ninja.lua +++ b/xmake/plugins/project/ninja/build_ninja.lua @@ -403,7 +403,6 @@ function _add_build_for_targets(ninjafile, outputdir) ninjafile:print("default default\n") end --- make function make(outputdir) -- enter project directory |
