summaryrefslogtreecommitdiff
path: root/xmake/plugins/project/make/makefile.lua
diff options
context:
space:
mode:
authorruki <[email protected]>2023-03-11 00:42:14 +0800
committerruki <[email protected]>2023-03-11 00:42:14 +0800
commitcb6f708d5644664168bee1071f48e350f406b438 (patch)
treea0f7afc62e8f7fd171ff04ad6b0e0b66e8f8f52d /xmake/plugins/project/make/makefile.lua
parentfdb8bc00233b61f17390f2ac60ae18d710a7a535 (diff)
rewrite makefile generator
Diffstat (limited to 'xmake/plugins/project/make/makefile.lua')
-rw-r--r--xmake/plugins/project/make/makefile.lua279
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()