1-- --
2-- make_cpp.lua
3-- Generate a C/C++ project makefile.
4-- Copyright (c) 2002-2013 Jason Perkins and the Premake project
5--
6
7	premake.make.cpp = { }
8	premake.make.override = { }
9	premake.make.makefile_ignore = false
10
11	local cpp = premake.make.cpp
12	local make = premake.make
13
14	function premake.make_cpp(prj)
15
16		-- create a shortcut to the compiler interface
17		local cc = premake.gettool(prj)
18
19		-- build a list of supported target platforms that also includes a generic build
20		local platforms = premake.filterplatforms(prj.solution, cc.platforms, "Native")
21
22		-- output build configurations
23		local action = premake.action.current()
24		premake.gmake_cpp_header(prj, cc, platforms)
25		premake.gmake_cpp_configs(prj, cc, platforms)
26		table.sort(prj.allfiles)
27
28		-- list object directories
29		local objdirs = {}
30		local additionalobjdirs = {}
31		for _, file in ipairs(prj.allfiles) do
32			if path.issourcefile(file) then
33				objdirs[_MAKE.esc(path.getdirectory(path.trimdots(file)))] = 1
34			end
35		end
36
37		for _, custombuildtask in ipairs(prj.custombuildtask or {}) do
38			for _, buildtask in ipairs(custombuildtask or {}) do
39				additionalobjdirs[_MAKE.esc(path.getdirectory(path.getrelative(prj.location,buildtask[2])))] = 1
40			end
41		end
42
43		_p('OBJDIRS := \\')
44		_p('\t$(OBJDIR) \\')
45		for dir, _ in iter.sortByKeys(objdirs) do
46			_p('\t$(OBJDIR)/%s \\', dir)
47		end
48		for dir, _ in iter.sortByKeys(additionalobjdirs) do
49			_p('\t%s \\', dir)
50		end
51		_p('')
52
53		_p('RESOURCES := \\')
54		for _, file in ipairs(prj.allfiles) do
55			if path.isresourcefile(file) then
56				_p('\t$(OBJDIR)/%s.res \\', _MAKE.esc(path.getbasename(file)))
57			end
58		end
59		_p('')
60
61		-- main build rule(s)
62		_p('.PHONY: clean prebuild prelink')
63		_p('')
64
65		if os.is("MacOSX") and prj.kind == "WindowedApp" and not prj.options.SkipBundling then
66			_p('all: $(OBJDIRS) $(TARGETDIR) prebuild prelink $(TARGET) $(dir $(TARGETDIR))PkgInfo $(dir $(TARGETDIR))Info.plist')
67		else
68			_p('all: $(OBJDIRS) $(TARGETDIR) prebuild prelink $(TARGET)')
69		end
70		_p('\t@:')
71		_p('')
72
73		if (prj.kind == "StaticLib" and prj.options.ArchiveSplit) then
74			_p('define max_args')
75			_p('\t$(eval _args:=)')
76			_p('\t$(foreach obj,$3,$(eval _args+=$(obj))$(if $(word $2,$(_args)),$1 $(_args)$(EOL)$(eval _args:=)))')
77			_p('\t$(if $(_args),$1 $(_args))')
78			_p('endef')
79			_p('')
80			_p('define EOL')
81			_p('')
82			_p('')
83			_p('endef')
84			_p('')
85		end
86
87		-- target build rule
88		_p('$(TARGET): $(GCH) $(OBJECTS) $(LIBDEPS) $(EXTERNAL_LIBS) $(RESOURCES) $(OBJRESP) $(LDRESP) | $(TARGETDIR) $(OBJDIRS)')
89
90		if prj.kind == "StaticLib" then
91			if prj.msgarchiving then
92				_p('\t@echo ' .. prj.msgarchiving)
93			else
94				_p('\t@echo Archiving %s', prj.name)
95			end
96			if (not prj.archivesplit_size) then
97				prj.archivesplit_size=200
98			end
99			if (not prj.options.ArchiveSplit) then
100				_p('ifeq (posix,$(SHELLTYPE))')
101				_p('\t$(SILENT) rm -f  $(TARGET)')
102				_p('else')
103				_p('\t$(SILENT) if exist $(subst /,\\\\,$(TARGET)) del $(subst /,\\\\,$(TARGET))')
104				_p('endif')
105				_p('\t$(SILENT) $(LINKCMD) $(LINKOBJS)' .. (os.is("MacOSX") and " 2>&1 > /dev/null | sed -e '/.o) has no symbols$$/d'" or ""))
106			else
107				_p('\t$(call RM,$(TARGET))')
108				_p('\t@$(call max_args,$(LINKCMD),'.. prj.archivesplit_size ..',$(LINKOBJS))' .. (os.is("MacOSX") and " 2>&1 > /dev/null | sed -e '/.o) has no symbols$$/d'" or ""))
109				_p('\t$(SILENT) $(LINKCMD_NDX)')
110			end
111		else
112			if prj.msglinking then
113				_p('\t@echo ' .. prj.msglinking)
114			else
115				_p('\t@echo Linking %s', prj.name)
116			end
117			_p('\t$(SILENT) $(LINKCMD)')
118		end
119
120		_p('\t$(POSTBUILDCMDS)')
121		_p('')
122
123		-- Create destination directories. Can't use $@ for this because it loses the
124		-- escaping, causing issues with spaces and parenthesis
125		_p('$(TARGETDIR):')
126		premake.make_mkdirrule("$(TARGETDIR)")
127
128		_p('$(OBJDIRS):')
129		if (not prj.solution.messageskip) or (not table.contains(prj.solution.messageskip, "SkipCreatingMessage")) then
130		_p('\t@echo Creating $(@)')
131		end
132		_p('\t-$(call MKDIR,$@)')
133		_p('')
134
135		-- Mac OS X specific targets
136		if os.is("MacOSX") and prj.kind == "WindowedApp" and not prj.options.SkipBundling then
137			_p('$(dir $(TARGETDIR))PkgInfo:')
138			_p('$(dir $(TARGETDIR))Info.plist:')
139			_p('')
140		end
141
142		-- clean target
143		_p('clean:')
144		if (not prj.solution.messageskip) or (not table.contains(prj.solution.messageskip, "SkipCleaningMessage")) then
145			_p('\t@echo Cleaning %s', prj.name)
146		end
147		_p('ifeq (posix,$(SHELLTYPE))')
148		_p('\t$(SILENT) rm -f  $(TARGET)')
149		_p('\t$(SILENT) rm -rf $(OBJDIR)')
150		_p('else')
151		_p('\t$(SILENT) if exist $(subst /,\\\\,$(TARGET)) del $(subst /,\\\\,$(TARGET))')
152		_p('\t$(SILENT) if exist $(subst /,\\\\,$(OBJDIR)) rmdir /s /q $(subst /,\\\\,$(OBJDIR))')
153		_p('endif')
154		_p('')
155
156		-- custom build step targets
157		_p('prebuild:')
158		_p('\t$(PREBUILDCMDS)')
159		_p('')
160
161		_p('prelink:')
162		_p('\t$(PRELINKCMDS)')
163		_p('')
164
165		-- precompiler header rule
166		cpp.pchrules(prj)
167
168		-- per-file build rules
169		cpp.fileRules(prj, cc)
170
171		-- per-dependency build rules
172		cpp.dependencyRules(prj)
173
174		for _, custombuildtask in ipairs(prj.custombuildtask or {}) do
175			for _, buildtask in ipairs(custombuildtask or {}) do
176				local deps =  string.format("%s ",path.getrelative(prj.location,buildtask[1]))
177				for _, depdata in ipairs(buildtask[3] or {}) do
178					deps = deps .. string.format("%s ",path.getrelative(prj.location,depdata))
179				end
180				_p('%s: %s | $(TARGETDIR) $(OBJDIRS)'
181					,path.getrelative(prj.location,buildtask[2])
182					, deps
183					)
184				for _, cmdline in ipairs(buildtask[4] or {}) do
185					local cmd = cmdline
186					local num = 1
187					for _, depdata in ipairs(buildtask[3] or {}) do
188						cmd = string.gsub(cmd,"%$%(" .. num .."%)", string.format("%s ",path.getrelative(prj.location,depdata)))
189						num = num + 1
190					end
191					cmd = string.gsub(cmd, "%$%(<%)", "$<")
192					cmd = string.gsub(cmd, "%$%(@%)", "$@")
193
194					_p('\t$(SILENT) %s',cmd)
195
196				end
197				_p('')
198			end
199		end
200
201		-- include the dependencies, built by GCC (with the -MMD flag)
202		_p('-include $(OBJECTS:%%.o=%%.d)')
203		_p('ifneq (,$(PCH))')
204			_p('  -include $(OBJDIR)/$(notdir $(PCH)).d')
205			_p('  -include $(OBJDIR)/$(notdir $(PCH))_objc.d')
206		_p('endif')
207	end
208
209
210
211--
212-- Write the makefile header
213--
214
215	function premake.gmake_cpp_header(prj, cc, platforms)
216		_p('# %s project makefile autogenerated by GENie', premake.action.current().shortname)
217
218		-- set up the environment
219		_p('ifndef config')
220		_p('  config=%s', _MAKE.esc(premake.getconfigname(prj.solution.configurations[1], platforms[1], true)))
221		_p('endif')
222		_p('')
223
224		_p('ifndef verbose')
225		_p('  SILENT = @')
226		_p('endif')
227		_p('')
228
229		-- identify the shell type
230		_p('SHELLTYPE := msdos')
231		_p('ifeq (,$(ComSpec)$(COMSPEC))')
232		_p('  SHELLTYPE := posix')
233		_p('endif')
234		_p('ifeq (/bin,$(findstring /bin,$(SHELL)))')
235		_p('  SHELLTYPE := posix')
236		_p('endif')
237		_p('ifeq (/bin,$(findstring /bin,$(MAKESHELL)))')
238		_p('  SHELLTYPE := posix')
239		_p('endif')
240		_p('')
241
242		_p('ifeq (posix,$(SHELLTYPE))')
243		_p('  MKDIR = $(SILENT) mkdir -p "$(1)"')
244		_p('  COPY  = $(SILENT) cp -fR "$(1)" "$(2)"')
245		_p('  RM    = $(SILENT) rm -f "$(1)"')
246		_p('else')
247		_p('  MKDIR = $(SILENT) mkdir "$(subst /,\\\\,$(1))" 2> nul || exit 0')
248		_p('  COPY  = $(SILENT) copy /Y "$(subst /,\\\\,$(1))" "$(subst /,\\\\,$(2))"')
249		_p('  RM    = $(SILENT) del /F "$(subst /,\\\\,$(1))" 2> nul || exit 0')
250		_p('endif')
251		_p('')
252
253		_p('CC  = %s', cc.cc)
254		_p('CXX = %s', cc.cxx)
255		_p('AR  = %s', cc.ar)
256		_p('')
257
258		_p('ifndef RESCOMP')
259		_p('  ifdef WINDRES')
260		_p('    RESCOMP = $(WINDRES)')
261		_p('  else')
262		_p('    RESCOMP = %s', cc.rc or 'windres')
263		_p('  endif')
264		_p('endif')
265		_p('')
266
267		if (not premake.make.makefile_ignore) then
268			_p('MAKEFILE = %s', _MAKE.getmakefilename(prj, true))
269			_p('')
270		end
271	end
272
273--
274-- Write a block of configuration settings.
275--
276
277	local function is_excluded(prj, cfg, file)
278		if table.icontains(prj.excludes, file) then
279			return true
280		end
281
282		if table.icontains(cfg.excludes, file) then
283			return true
284		end
285
286		return false
287	end
288
289	function premake.gmake_cpp_configs(prj, cc, platforms)
290		for _, platform in ipairs(platforms) do
291			for cfg in premake.eachconfig(prj, platform) do
292				premake.gmake_cpp_config(prj, cfg, cc)
293			end
294		end
295	end
296
297	function premake.gmake_cpp_config(prj, cfg, cc)
298		_p('ifeq ($(config),%s)', _MAKE.esc(cfg.shortname))
299
300		-- if this platform requires a special compiler or linker, list it here
301		cpp.platformtools(cfg, cc)
302
303		local targetDir = _MAKE.esc(cfg.buildtarget.directory)
304
305		_p('  ' .. (table.contains(premake.make.override,"OBJDIR") and "override " or "") ..    'OBJDIR              = %s', _MAKE.esc(cfg.objectsdir))
306		_p('  ' .. (table.contains(premake.make.override,"TARGETDIR") and "override " or "") .. 'TARGETDIR           = %s', iif(targetDir == "", ".", targetDir))
307		_p('  ' .. (table.contains(premake.make.override,"TARGET") and "override " or "") ..    'TARGET              = $(TARGETDIR)/%s', _MAKE.esc(cfg.buildtarget.name))
308		_p('  DEFINES            +=%s', make.list(_MAKE.escquote(cc.getdefines(cfg.defines))))
309
310		local id  = make.list(cc.getincludedirs(cfg.includedirs));
311		local uid = make.list(cc.getquoteincludedirs(cfg.userincludedirs))
312		local sid = make.list(cc.getsystemincludedirs(cfg.systemincludedirs))
313
314		if id ~= "" then
315			_p('  INCLUDES           +=%s', id)
316		end
317
318		if uid ~= "" then
319			_p('  INCLUDES           +=%s', uid)
320		end
321
322		if sid ~= "" then
323			_p('  INCLUDES           +=%s', sid)
324		end
325
326		-- set up precompiled headers
327		cpp.pchconfig(cfg)
328
329		-- CPPFLAGS, CFLAGS, CXXFLAGS, and RESFLAGS
330		cpp.flags(cfg, cc)
331
332		-- write out libraries, linker flags, and the link command
333		cpp.linker(prj, cfg, cc)
334
335		table.sort(cfg.files)
336
337		-- add objects for compilation, and remove any that are excluded per config.
338		if cfg.flags.UseObjectResponseFile then
339			_p('  OBJRESP             = $(OBJDIR)/%s_objects', prj.name)
340		else
341			_p('  OBJRESP             =')
342		end
343		_p('  OBJECTS := \\')
344		for _, file in ipairs(cfg.files) do
345			if path.issourcefile(file) then
346				-- check if file is excluded.
347				if not is_excluded(prj, cfg, file) then
348					-- if not excluded, add it.
349					_p('\t$(OBJDIR)/%s.o \\'
350						, _MAKE.esc(path.trimdots(path.removeext(file)))
351						)
352				end
353			end
354		end
355		_p('')
356
357		_p('  define PREBUILDCMDS')
358		if #cfg.prebuildcommands > 0 then
359			_p('\t@echo Running pre-build commands')
360			_p('\t%s', table.implode(cfg.prebuildcommands, "", "", "\n\t"))
361		end
362		_p('  endef')
363
364		_p('  define PRELINKCMDS')
365		if #cfg.prelinkcommands > 0 then
366			_p('\t@echo Running pre-link commands')
367			_p('\t%s', table.implode(cfg.prelinkcommands, "", "", "\n\t"))
368		end
369		_p('  endef')
370
371		_p('  define POSTBUILDCMDS')
372		if #cfg.postbuildcommands > 0 then
373			_p('\t@echo Running post-build commands')
374			_p('\t%s', table.implode(cfg.postbuildcommands, "", "", "\n\t"))
375		end
376		_p('  endef')
377
378		-- write out config-level makesettings blocks
379		make.settings(cfg, cc)
380
381		_p('endif')
382		_p('')
383	end
384
385
386--
387-- Platform support
388--
389
390	function cpp.platformtools(cfg, cc)
391		local platform = cc.platforms[cfg.platform]
392		if platform.cc then
393			_p('  CC         = %s', platform.cc)
394		end
395		if platform.cxx then
396			_p('  CXX        = %s', platform.cxx)
397		end
398		if platform.ar then
399			_p('  AR         = %s', platform.ar)
400		end
401	end
402
403
404--
405-- Configurations
406--
407
408	function cpp.flags(cfg, cc)
409
410		if cfg.pchheader and not cfg.flags.NoPCH then
411			_p('  FORCE_INCLUDE      += -include $(OBJDIR)/$(notdir $(PCH))')
412			_p('  FORCE_INCLUDE_OBJC += -include $(OBJDIR)/$(notdir $(PCH))_objc')
413		end
414
415		if #cfg.forcedincludes > 0 then
416			_p('  FORCE_INCLUDE      += -include %s'
417					,_MAKE.esc(table.concat(cfg.forcedincludes, ";")))
418		end
419
420		_p('  ALL_CPPFLAGS       += $(CPPFLAGS) %s $(DEFINES) $(INCLUDES)', table.concat(cc.getcppflags(cfg), " "))
421
422		_p('  ALL_ASMFLAGS       += $(ASMFLAGS) $(CFLAGS) $(ALL_CPPFLAGS) $(ARCH)%s', make.list(table.join(cc.getcflags(cfg), cfg.buildoptions, cfg.buildoptions_asm)))
423		_p('  ALL_CFLAGS         += $(CFLAGS) $(ALL_CPPFLAGS) $(ARCH)%s', make.list(table.join(cc.getcflags(cfg), cfg.buildoptions, cfg.buildoptions_c)))
424		_p('  ALL_CXXFLAGS       += $(CXXFLAGS) $(CFLAGS) $(ALL_CPPFLAGS) $(ARCH)%s', make.list(table.join(cc.getcflags(cfg), cc.getcxxflags(cfg), cfg.buildoptions, cfg.buildoptions_cpp)))
425		_p('  ALL_OBJCFLAGS      += $(CFLAGS) $(ALL_CPPFLAGS) $(ARCH)%s', make.list(table.join(cc.getcflags(cfg), cc.getobjcflags(cfg), cfg.buildoptions, cfg.buildoptions_objc)))
426		_p('  ALL_OBJCPPFLAGS    += $(CXXFLAGS) $(CFLAGS) $(ALL_CPPFLAGS) $(ARCH)%s', make.list(table.join(cc.getcflags(cfg), cc.getcxxflags(cfg), cc.getobjcflags(cfg), cfg.buildoptions, cfg.buildoptions_objcpp)))
427
428		_p('  ALL_RESFLAGS       += $(RESFLAGS) $(DEFINES) $(INCLUDES)%s',
429		        make.list(table.join(cc.getdefines(cfg.resdefines),
430		                                cc.getincludedirs(cfg.resincludedirs), cfg.resoptions)))
431	end
432
433
434--
435-- Linker settings, including the libraries to link, the linker flags,
436-- and the linker command.
437--
438
439	function cpp.linker(prj, cfg, cc)
440		local libdeps
441		local lddeps
442
443		if #cfg.wholearchive > 0 then
444			libdeps = {}
445			lddeps  = {}
446
447			for _, linkcfg in ipairs(premake.getlinks(cfg, "siblings", "object")) do
448				local linkpath = path.rebase(linkcfg.linktarget.fullpath, linkcfg.location, cfg.location)
449
450				if table.icontains(cfg.wholearchive, linkcfg.project.name) then
451					lddeps = table.join(lddeps, cc.wholearchive(linkpath))
452				else
453					table.insert(lddeps, linkpath)
454				end
455
456				table.insert(libdeps, linkpath)
457			end
458
459			libdeps = make.list(_MAKE.esc(libdeps))
460			lddeps  = make.list(_MAKE.esc(lddeps))
461		else
462			libdeps = make.list(_MAKE.esc(premake.getlinks(cfg, "siblings", "fullpath")))
463			lddeps  = libdeps
464		end
465
466		_p('  ALL_LDFLAGS        += $(LDFLAGS)%s', make.list(table.join(cc.getlibdirflags(cfg), cc.getldflags(cfg), cfg.linkoptions)))
467		_p('  LIBDEPS            +=%s', libdeps)
468		_p('  LDDEPS             +=%s', lddeps)
469
470		if cfg.flags.UseLDResponseFile then
471			_p('  LDRESP              = $(OBJDIR)/%s_libs', prj.name)
472			_p('  LIBS               += @$(LDRESP)%s', make.list(cc.getlinkflags(cfg)))
473		else
474			_p('  LDRESP              =')
475			_p('  LIBS               += $(LDDEPS)%s', make.list(cc.getlinkflags(cfg)))
476		end
477
478		_p('  EXTERNAL_LIBS      +=%s', make.list(cc.getlibfiles(cfg)))
479		_p('  LINKOBJS            = %s', (cfg.flags.UseObjectResponseFile and "@$(OBJRESP)" or "$(OBJECTS)"))
480
481		if cfg.kind == "StaticLib" then
482			if (not prj.options.ArchiveSplit) then
483				_p('  LINKCMD             = $(AR) %s $(TARGET)', make.list(cc.getarchiveflags(prj, cfg, false)))
484			else
485				_p('  LINKCMD             = $(AR) %s $(TARGET)', make.list(cc.getarchiveflags(prj, cfg, false)))
486				_p('  LINKCMD_NDX         = $(AR) %s $(TARGET)', make.list(cc.getarchiveflags(prj, cfg, true)))
487			end
488		else
489			local tool = iif(cfg.language == "C", "CC", "CXX")
490			local startgroup = ''
491			local endgroup = ''
492			if (cfg.flags.LinkSupportCircularDependencies) then
493				startgroup = '-Wl,--start-group '
494				endgroup   = ' -Wl,--end-group'
495			end
496			_p('  LINKCMD             = $(%s) -o $(TARGET) $(LINKOBJS) $(RESOURCES) $(ARCH) $(ALL_LDFLAGS) %s$(LIBS)%s', tool, startgroup, endgroup)
497		end
498	end
499
500
501--
502-- Precompiled header support
503--
504
505	function cpp.pchconfig(cfg)
506
507		-- If there is no header, or if PCH has been disabled, I can early out
508
509		if not cfg.pchheader or cfg.flags.NoPCH then
510			return
511		end
512
513		-- Visual Studio requires the PCH header to be specified in the same way
514		-- it appears in the #include statements used in the source code; the PCH
515		-- source actual handles the compilation of the header. GCC compiles the
516		-- header file directly, and needs the file's actual file system path in
517		-- order to locate it.
518
519		-- To maximize the compatibility between the two approaches, see if I can
520		-- locate the specified PCH header on one of the include file search paths
521		-- and, if so, adjust the path automatically so the user doesn't have
522		-- add a conditional configuration to the project script.
523
524		local pch = cfg.pchheader
525		for _, incdir in ipairs(cfg.includedirs) do
526
527			-- convert this back to an absolute path for os.isfile()
528			local abspath = path.getabsolute(path.join(cfg.project.location, incdir))
529
530			local testname = path.join(abspath, pch)
531			if os.isfile(testname) then
532				pch = path.getrelative(cfg.location, testname)
533				break
534			end
535		end
536
537		_p('  PCH                 = %s', _MAKE.esc(pch))
538		_p('  GCH                 = $(OBJDIR)/$(notdir $(PCH)).gch')
539		_p('  GCH_OBJC            = $(OBJDIR)/$(notdir $(PCH))_objc.gch')
540
541	end
542
543
544	function cpp.pchrules(prj)
545		_p('ifneq (,$(PCH))')
546		_p('$(GCH): $(PCH) $(MAKEFILE) | $(OBJDIR)')
547		if prj.msgprecompile then
548			_p('\t@echo ' .. prj.msgprecompile)
549		else
550			_p('\t@echo $(notdir $<)')
551		end
552
553		local cmd = iif(prj.language == "C", "$(CC) $(ALL_CFLAGS) -x c-header", "$(CXX) $(ALL_CXXFLAGS) -x c++-header")
554		_p('\t$(SILENT) %s $(DEFINES) $(INCLUDES) -o "$@" -c "$<"', cmd)
555
556		_p('')
557
558		_p('$(GCH_OBJC): $(PCH) $(MAKEFILE) | $(OBJDIR)')
559		if prj.msgprecompile then
560			_p('\t@echo ' .. prj.msgprecompile)
561		else
562			_p('\t@echo $(notdir $<)')
563		end
564
565		local cmd = iif(prj.language == "C", "$(CC) $(ALL_OBJCFLAGS) -x objective-c-header", "$(CXX) $(ALL_OBJCPPFLAGS) -x objective-c++-header")
566		_p('\t$(SILENT) %s $(DEFINES) $(INCLUDES) -o "$@" -c "$<"', cmd)
567
568		_p('endif')
569		_p('')
570	end
571
572
573--
574-- Build command for a single file.
575--
576
577	function cpp.fileRules(prj, cc)
578		local platforms = premake.filterplatforms(prj.solution, cc.platforms, "Native")
579
580		_p('ifneq (,$(OBJRESP))')
581		_p('$(OBJRESP): $(OBJECTS) | $(TARGETDIR) $(OBJDIRS)')
582		_p('\t$(SILENT) echo $^')
583		_p('\t$(SILENT) echo $^ > $@')
584		_p('endif')
585		_p('')
586
587		_p('ifneq (,$(LDRESP))')
588		_p('$(LDRESP): $(LDDEPS) | $(TARGETDIR) $(OBJDIRS)')
589		_p('\t$(SILENT) echo $^')
590		_p('\t$(SILENT) echo $^ > $@')
591		_p('endif')
592		_p('')
593
594		table.sort(prj.allfiles)
595		for _, file in ipairs(prj.allfiles or {}) do
596			if path.issourcefile(file) then
597				if (path.isobjcfile(file)) then
598					_p('$(OBJDIR)/%s.o: %s $(GCH_OBJC) $(MAKEFILE) | $(OBJDIR)/%s'
599						, _MAKE.esc(path.trimdots(path.removeext(file)))
600						, _MAKE.esc(file)
601						, _MAKE.esc(path.getdirectory(path.trimdots(file)))
602						)
603				else
604					_p('$(OBJDIR)/%s.o: %s $(GCH) $(MAKEFILE) | $(OBJDIR)/%s'
605						, _MAKE.esc(path.trimdots(path.removeext(file)))
606						, _MAKE.esc(file)
607						, _MAKE.esc(path.getdirectory(path.trimdots(file)))
608						)
609				end
610				if (path.isobjcfile(file) and prj.msgcompile_objc) then
611					_p('\t@echo ' .. prj.msgcompile_objc)
612				elseif prj.msgcompile then
613					_p('\t@echo ' .. prj.msgcompile)
614				else
615					_p('\t@echo $(notdir $<)')
616				end
617				if (path.isobjcfile(file)) then
618					if (path.iscfile(file)) then
619						_p('\t$(SILENT) $(CXX) $(ALL_OBJCFLAGS) $(FORCE_INCLUDE_OBJC) -o "$@" -c "$<"')
620					else
621						_p('\t$(SILENT) $(CXX) $(ALL_OBJCPPFLAGS) $(FORCE_INCLUDE_OBJC) -o "$@" -c "$<"')
622					end
623				elseif (path.isasmfile(file)) then
624					_p('\t$(SILENT) $(CC) $(ALL_ASMFLAGS) -o "$@" -c "$<"')
625				else
626					cpp.buildcommand(path.iscfile(file) and not prj.options.ForceCPP, "o")
627				end
628				for _, task in ipairs(prj.postcompiletasks or {}) do
629					_p('\t$(SILENT) %s', task)
630					_p('')
631				end
632
633				_p('')
634			elseif (path.getextension(file) == ".rc") then
635				_p('$(OBJDIR)/%s.res: %s', _MAKE.esc(path.getbasename(file)), _MAKE.esc(file))
636				if prj.msgresource then
637					_p('\t@echo ' .. prj.msgresource)
638				else
639					_p('\t@echo $(notdir $<)')
640				end
641				_p('\t$(SILENT) $(RESCOMP) $< -O coff -o "$@" $(ALL_RESFLAGS)')
642				_p('')
643			end
644		end
645	end
646
647	function cpp.dependencyRules(prj)
648		for _, dependency in ipairs(prj.dependency or {}) do
649			for _, dep in ipairs(dependency or {}) do
650				if (dep[3]==nil or dep[3]==false) then
651					_p('$(OBJDIR)/%s.o: %s'
652						, _MAKE.esc(path.trimdots(path.removeext(path.getrelative(prj.location, dep[1]))))
653						, _MAKE.esc(path.getrelative(prj.location, dep[2]))
654						)
655				else
656					_p('%s: %s'
657						, _MAKE.esc(dep[1])
658						, _MAKE.esc(path.getrelative(prj.location, dep[2]))
659						)
660				end
661				_p('')
662			end
663		end
664	end
665
666
667	function cpp.buildcommand(iscfile, objext)
668		local flags = iif(iscfile, '$(CC) $(ALL_CFLAGS)', '$(CXX) $(ALL_CXXFLAGS)')
669		_p('\t$(SILENT) %s $(FORCE_INCLUDE) -o "$@" -c "$<"', flags, objext)
670	end
671