diff options
| author | ruki <[email protected]> | 2023-03-11 00:42:14 +0800 |
|---|---|---|
| committer | ruki <[email protected]> | 2023-03-11 00:42:14 +0800 |
| commit | cb6f708d5644664168bee1071f48e350f406b438 (patch) | |
| tree | a0f7afc62e8f7fd171ff04ad6b0e0b66e8f8f52d /xmake/plugins/project/make/makefile.lua | |
| parent | fdb8bc00233b61f17390f2ac60ae18d710a7a535 (diff) | |
rewrite makefile generator
Diffstat (limited to 'xmake/plugins/project/make/makefile.lua')
| -rw-r--r-- | xmake/plugins/project/make/makefile.lua | 279 |
1 files changed, 150 insertions, 129 deletions
diff --git a/xmake/plugins/project/make/makefile.lua b/xmake/plugins/project/make/makefile.lua index 87c3b6f55..880e59982 100644 --- a/xmake/plugins/project/make/makefile.lua +++ b/xmake/plugins/project/make/makefile.lua @@ -26,44 +26,65 @@ import("core.language.language") import("core.platform.platform") import("lib.detect.find_tool") --- get log makefile -function _logfile() - return vformat("$(buildir)/.build.log") +-- escape path +function _escape_path(filepath) + if is_host("windows") then + filepath = filepath:gsub('\\', '/') + end + return filepath 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 = _escape_path(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 +99,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 +146,56 @@ 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 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 +209,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 +251,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@%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 +269,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 +286,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 +314,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 +341,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 +364,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@%s\n", command) makefile:print("") -- build source batches @@ -327,19 +374,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 "") +-- add build +function _add_build(makefile, outputdir) -- make variables for ccache local ccache = find_tool("ccache") @@ -376,35 +417,35 @@ function _make_all(makefile) target:set("pcxxheader", nil) end - -- make variables for target + -- add variables for target local targetflags = {} for targetname, target in pairs(project.targets()) do if not target:is_phony() then - -- make target linker + -- add 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 + -- add target flags for _, sourcebatch in pairs(target:sourcebatches()) do local sourcekind = sourcebatch.sourcekind if sourcekind then - -- make source compiler + -- add 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)) + -- add source flags + 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(target:linkflags())) + makefile:print("%s_%sFLAGS=%s", targetname, target:linker():kind():upper(), os.args(_translate_linkflags(target:linkflags(), outputdir))) end end makefile:print("") @@ -424,80 +465,60 @@ function _make_all(makefile) makefile:print("all: %s\n", all) makefile:print(".PHONY: default all %s\n", all) - -- make it for all targets + -- add build 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) - - -- make head +-- add clean target +function _add_clean_target(makefile, target, outputdir) makefile:printf("clean_%s: ", target:name()) - - -- make dependence for the dependent targets 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 +function _add_clean(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 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 build + _add_build(makefile, outputdir) - -- clean all - _clean_all(makefile) + -- add clean + _add_clean(makefile, outputdir) -- close the makefile makefile:close() |
