1--
2-- make_csharp.lua
3-- Generate a C# project makefile.
4-- Copyright (c) 2002-2013 Jason Perkins and the Premake project
5--
6
7	local p = premake
8	p.make.cs = {}
9	local make = p.make
10	local cs = p.make.cs
11	local project = p.project
12	local config = p.config
13	local fileconfig = p.fileconfig
14
15
16--
17-- Add namespace for element definition lists for p.callarray()
18--
19
20	cs.elements = {}
21
22
23--
24-- Generate a GNU make C++ project makefile, with support for the new platforms API.
25--
26
27	cs.elements.makefile = function(prj)
28		return {
29			make.header,
30			make.phonyRules,
31			make.csConfigs,
32			make.csProjectConfig,
33			make.csSources,
34			make.csEmbedFiles,
35			make.csCopyFiles,
36			make.csResponseFile,
37			make.shellType,
38			make.csAllRules,
39			make.csTargetRules,
40			make.targetDirRules,
41			make.csResponseRules,
42			make.objDirRules,
43			make.csCleanRules,
44			make.preBuildRules,
45			make.preLinkRules,
46			make.csFileRules,
47		}
48	end
49
50
51--
52-- Generate a GNU make C# project makefile, with support for the new platforms API.
53--
54
55	function make.cs.generate(prj)
56		p.eol("\n")
57		local toolset = p.tools.dotnet
58		p.callArray(cs.elements.makefile, prj, toolset)
59	end
60
61
62--
63-- Write out the settings for a particular configuration.
64--
65
66	cs.elements.configuration = function(cfg)
67		return {
68			make.csTools,
69			make.target,
70			make.objdir,
71			make.csFlags,
72			make.csLinkCmd,
73			make.preBuildCmds,
74			make.preLinkCmds,
75			make.postBuildCmds,
76			make.settings,
77		}
78	end
79
80	function make.csConfigs(prj, toolset)
81		for cfg in project.eachconfig(prj) do
82			_x('ifeq ($(config),%s)', cfg.shortname)
83			p.callArray(cs.elements.configuration, cfg, toolset)
84			_p('endif')
85			_p('')
86		end
87	end
88
89
90--
91-- Given a .resx resource file, builds the path to corresponding .resource
92-- file, matching the behavior and naming of Visual Studio.
93--
94
95	function cs.getresourcefilename(cfg, fname)
96		if path.getextension(fname) == ".resx" then
97			local name = cfg.buildtarget.basename .. "."
98			local dir = path.getdirectory(fname)
99			if dir ~= "." then
100				name = name .. path.translate(dir, ".") .. "."
101			end
102			return "$(OBJDIR)/" .. p.esc(name .. path.getbasename(fname)) .. ".resources"
103		else
104			return fname
105		end
106	end
107
108
109--
110-- Iterate and output some selection of the source code files.
111--
112
113	function cs.listsources(prj, selector)
114		local tr = project.getsourcetree(prj)
115		p.tree.traverse(tr, {
116			onleaf = function(node, depth)
117				local value = selector(node)
118				if value then
119					_x('\t%s \\', value)
120				end
121			end
122		})
123	end
124
125
126
127
128
129---------------------------------------------------------------------------
130--
131-- Handlers for individual makefile elements
132--
133---------------------------------------------------------------------------
134
135	function make.csAllRules(prj, toolset)
136		_p('all: $(TARGETDIR) $(OBJDIR) prebuild $(EMBEDFILES) $(COPYFILES) prelink $(TARGET)')
137		_p('')
138	end
139
140
141	function make.csCleanRules(prj, toolset)
142		--[[
143		-- porting from 4.x
144		_p('clean:')
145		_p('\t@echo Cleaning %s', prj.name)
146		_p('ifeq (posix,$(SHELLTYPE))')
147		_p('\t$(SILENT) rm -f $(TARGETDIR)/%s.* $(COPYFILES)', target.basename)
148		_p('\t$(SILENT) rm -rf $(OBJDIR)')
149		_p('else')
150		_p('\t$(SILENT) if exist $(subst /,\\\\,$(TARGETDIR)/%s) del $(subst /,\\\\,$(TARGETDIR)/%s.*)', target.name, target.basename)
151		for target, source in pairs(cfgpairs[anycfg]) do
152			_p('\t$(SILENT) if exist $(subst /,\\\\,%s) del $(subst /,\\\\,%s)', target, target)
153		end
154		for target, source in pairs(copypairs) do
155			_p('\t$(SILENT) if exist $(subst /,\\\\,%s) del $(subst /,\\\\,%s)', target, target)
156		end
157		_p('\t$(SILENT) if exist $(subst /,\\\\,$(OBJDIR)) rmdir /s /q $(subst /,\\\\,$(OBJDIR))')
158		_p('endif')
159		_p('')
160		--]]
161	end
162
163
164	function make.csCopyFiles(prj, toolset)
165		--[[
166		-- copied from 4.x; needs more porting
167		_p('COPYFILES += \\')
168		for target, source in pairs(cfgpairs[anycfg]) do
169			_p('\t%s \\', target)
170		end
171		for target, source in pairs(copypairs) do
172			_p('\t%s \\', target)
173		end
174		_p('')
175		--]]
176	end
177
178
179	function make.cs.getresponsefilename(prj)
180		return '$(OBJDIR)/' .. prj.filename .. '.rsp'
181	end
182
183
184	function make.csResponseFile(prj, toolset)
185		_x('RESPONSE += ' .. make.cs.getresponsefilename(prj))
186	end
187
188
189	function make.csResponseRules(prj)
190		local toolset = p.tools.dotnet
191		local ext = make.getmakefilename(prj, true)
192		local makefile = path.getname(p.filename(prj, ext))
193		local response = make.cs.getresponsefilename(prj)
194
195		_p('$(RESPONSE): %s', makefile)
196		_p('\t@echo Generating response file', prj.name)
197
198		_p('ifeq (posix,$(SHELLTYPE))')
199			_x('\t$(SILENT) rm -f $(RESPONSE)')
200		_p('else')
201			_x('\t$(SILENT) if exist $(RESPONSE) del %s', path.translate(response, '\\'))
202		_p('endif')
203
204		local sep = os.istarget("windows") and "\\" or "/"
205		local tr = project.getsourcetree(prj)
206		p.tree.traverse(tr, {
207			onleaf = function(node, depth)
208				if toolset.fileinfo(node).action == "Compile" then
209					_x('\t@echo %s >> $(RESPONSE)', path.translate(node.relpath, sep))
210				end
211			end
212		})
213		_p('')
214	end
215
216
217	function make.csEmbedFiles(prj, toolset)
218		local cfg = project.getfirstconfig(prj)
219
220		_p('EMBEDFILES += \\')
221		cs.listsources(prj, function(node)
222			local fcfg = fileconfig.getconfig(node, cfg)
223			local info = toolset.fileinfo(fcfg)
224			if info.action == "EmbeddedResource" then
225				return cs.getresourcefilename(cfg, node.relpath)
226			end
227		end)
228		_p('')
229	end
230
231
232	function make.csFileRules(prj, toolset)
233		--[[
234		-- porting from 4.x
235		_p('# Per-configuration copied file rules')
236		for cfg in p.eachconfig(prj) do
237			_x('ifneq (,$(findstring %s,$(config)))', cfg.name:lower())
238			for target, source in pairs(cfgpairs[cfg]) do
239				p.make_copyrule(source, target)
240			end
241			_p('endif')
242			_p('')
243		end
244
245		_p('# Copied file rules')
246		for target, source in pairs(copypairs) do
247			p.make_copyrule(source, target)
248		end
249
250		_p('# Embedded file rules')
251		for _, fname in ipairs(embedded) do
252			if path.getextension(fname) == ".resx" then
253				_x('%s: %s', getresourcefilename(prj, fname), fname)
254				_p('\t$(SILENT) $(RESGEN) $^ $@')
255			end
256			_p('')
257		end
258		--]]
259	end
260
261
262	function make.csFlags(cfg, toolset)
263		_p('  FLAGS =%s', make.list(toolset.getflags(cfg)))
264	end
265
266
267	function make.csLinkCmd(cfg, toolset)
268		local deps = p.esc(config.getlinks(cfg, "dependencies", "fullpath"))
269		_p('  DEPENDS =%s', make.list(deps))
270		_p('  REFERENCES = %s', table.implode(deps, "/r:", "", " "))
271	end
272
273
274	function make.csProjectConfig(prj, toolset)
275		-- To maintain compatibility with Visual Studio, these values must
276		-- be set on the project level, and not per-configuration.
277		local cfg = project.getfirstconfig(prj)
278
279		local kindflag = "/t:" .. toolset.getkind(cfg):lower()
280		local libdirs = table.implode(p.esc(cfg.libdirs), "/lib:", "", " ")
281		_p('FLAGS += %s', table.concat(table.join(kindflag, libdirs), " "))
282
283		local refs = p.esc(config.getlinks(cfg, "system", "fullpath"))
284		_p('REFERENCES += %s', table.implode(refs, "/r:", "", " "))
285		_p('')
286	end
287
288
289	function make.csSources(prj, toolset)
290		local cfg = project.getfirstconfig(prj)
291
292		_p('SOURCES += \\')
293		cs.listsources(prj, function(node)
294			local fcfg = fileconfig.getconfig(node, cfg)
295			local info = toolset.fileinfo(fcfg)
296			if info.action == "Compile" then
297				return node.relpath
298			end
299		end)
300		_p('')
301	end
302
303
304	function make.csTargetRules(prj, toolset)
305		_p('$(TARGET): $(SOURCES) $(EMBEDFILES) $(DEPENDS) $(RESPONSE)')
306		_p('\t$(SILENT) $(CSC) /nologo /out:$@ $(FLAGS) $(REFERENCES) @$(RESPONSE) $(patsubst %%,/resource:%%,$(EMBEDFILES))')
307		_p('\t$(POSTBUILDCMDS)')
308		_p('')
309	end
310
311
312	function make.csTools(cfg, toolset)
313		_p('  CSC = %s', toolset.gettoolname(cfg, "csc"))
314		_p('  RESGEN = %s', toolset.gettoolname(cfg, "resgen"))
315	end
316