1--
2-- vs2005_dotnetbase.lua
3-- Generate a Visual Studio 2005+ .NET project.
4-- Copyright (c) Jason Perkins and the Premake project
5--
6
7	local p = premake
8	p.vstudio.dotnetbase = {}
9
10	local vstudio = p.vstudio
11	local dotnetbase  = p.vstudio.dotnetbase
12	local project = p.project
13	local config = p.config
14	local fileconfig = p.fileconfig
15	local dotnet = p.tools.dotnet
16
17
18	dotnetbase.elements = {}
19	dotnetbase.langObj = {}
20	dotnetbase.netcore = {}
21
22--
23-- Generate a Visual Studio 200x dotnet project, with support for the new platforms API.
24--
25
26	function dotnetbase.prepare(langObj)
27		dotnetbase.elements.project = langObj.elements.project
28		dotnetbase.elements.projectProperties = langObj.elements.projectProperties
29		dotnetbase.elements.configuration = langObj.elements.configuration
30
31		dotnetbase.langObj = langObj
32	end
33
34
35	function dotnetbase.generate(prj)
36		p.utf8()
37
38		p.callArray(dotnetbase.elements.project, prj)
39
40		_p(1,'<ItemGroup>')
41		dotnetbase.files(prj)
42		_p(1,'</ItemGroup>')
43
44		dotnetbase.projectReferences(prj)
45		dotnetbase.packageReferences(prj)
46		dotnetbase.langObj.targets(prj)
47		dotnetbase.buildEvents(prj)
48
49		p.out('</Project>')
50	end
51
52
53--
54-- Write the opening <Project> element.
55--
56
57	function dotnetbase.projectElement(prj)
58		if dotnetbase.isNewFormatProject(prj) then
59			if prj.flags.WPF then
60				_p('<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">')
61			else
62				_p('<Project Sdk="Microsoft.NET.Sdk">')
63			end
64		else
65			local ver = ''
66			local action = p.action.current()
67			if action.vstudio.toolsVersion then
68				ver = string.format(' ToolsVersion="%s"', action.vstudio.toolsVersion)
69			end
70			_p('<Project%s DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">', ver)
71		end
72	end
73
74
75--
76-- Write the opening PropertyGroup, which contains the project-level settings.
77--
78
79
80	function dotnetbase.projectProperties(prj)
81		_p(1,'<PropertyGroup>')
82		local cfg = project.getfirstconfig(prj)
83		p.callArray(dotnetbase.elements.projectProperties, cfg)
84		_p(1,'</PropertyGroup>')
85	end
86
87
88--
89-- Write out the settings for the project configurations.
90--
91
92
93	function dotnetbase.configurations(prj)
94		for cfg in project.eachconfig(prj) do
95			dotnetbase.configuration(cfg)
96		end
97	end
98
99	function dotnetbase.configuration(cfg)
100		p.callArray(dotnetbase.elements.configuration, cfg)
101		_p(1,'</PropertyGroup>')
102	end
103
104
105	function dotnetbase.dofile(node, cfg, condition)
106		local filecfg = fileconfig.getconfig(node, cfg)
107		if filecfg then
108			local fname = path.translate(node.relpath)
109
110			-- Files that live outside of the project tree need to be "linked"
111			-- and provided with a project relative pseudo-path. Check for any
112			-- leading "../" sequences and, if found, remove them and mark this
113			-- path as external.
114			local link, count = node.relpath:gsub("%.%.%/", "")
115			local external = (count > 0)
116
117			-- Try to provide a little bit of flexibility by allowing virtual
118			-- paths for external files. Would be great to support them for all
119			-- files but Visual Studio chokes if file is already in project area.
120			if external and node.vpath ~= node.relpath then
121				link = node.vpath
122			end
123
124			-- Deduce what, if any, special attributes are required for this file.
125			-- For example, forms may have related source, designer, and resource
126			-- files which need to be associated.
127
128			local info = dotnet.fileinfo(filecfg)
129
130			-- Process any sub-elements required by this file; choose the write
131			-- element form to use based on the results.
132
133			local contents = p.capture(function ()
134				-- Try to write file-level elements in the same order as Visual Studio
135				local elements = {
136					"AutoGen",
137					"CopyToOutputDirectory",
138					"DesignTime",
139					"DependentUpon",
140					"DesignTimeSharedInput",
141					"Generator",
142					"LastGenOutput",
143					"SubType",
144				}
145
146				for _, el in ipairs(elements) do
147					local value = info[el]
148					if value then
149						_p(3,"<%s>%s</%s>", el, value, el)
150					end
151				end
152				if info.action == "EmbeddedResource" and cfg.customtoolnamespace then
153					_p(3,"<CustomToolNamespace>%s</CustomToolNamespace>", cfg.customtoolnamespace)
154				end
155			end)
156
157			if #contents > 0 or external then
158				_p(2,'<%s%s Include="%s">', info.action, condition, fname)
159				if external and info.action ~= "EmbeddedResource" then
160					_p(3,'<Link>%s</Link>', path.translate(link))
161				end
162				if #contents > 0 then
163					_p("%s", contents)
164				end
165				_p(2,'</%s>', info.action)
166			else
167				_p(2,'<%s%s Include="%s" />', info.action, condition, fname)
168			end
169		end
170	end
171
172
173--
174-- Write out the source files item group.
175--
176
177	function dotnetbase.files(prj)
178		local firstcfg = project.getfirstconfig(prj)
179
180		local processfcfg = function(node)
181			-- test if all fileinfo's are going to be the same for each config.
182			local allsame = true
183			local first = nil
184			for cfg in project.eachconfig(prj) do
185				local filecfg = fileconfig.getconfig(node, cfg)
186				local info = dotnet.fileinfo(filecfg)
187
188				if first == nil then
189					first = info
190				elseif not table.equals(first, info) then
191					allsame = false
192				end
193			end
194
195			-- output to proj file.
196			if allsame then
197				dotnetbase.dofile(node, firstcfg, '')
198			else
199				for cfg in project.eachconfig(prj) do
200					dotnetbase.dofile(node, cfg, ' ' .. dotnetbase.condition(cfg))
201				end
202			end
203		end
204
205		if project.isfsharp(prj) then
206			sorter = function(a,b)
207				verbosef('Sorting F# proj file (%s, %s), index %s < %s', a.name, b.name, a.order, b.order)
208				return a.order < b.order
209			end
210
211			table.sort(prj._.files, sorter)
212			table.foreachi(prj._.files, processfcfg)
213		else
214			local tr = project.getsourcetree(prj)
215			p.tree.traverse(tr, {
216				onleaf = processfcfg
217			}, false)
218		end
219	end
220
221
222--
223-- Write out pre- and post-build events, if provided.
224--
225
226	function dotnetbase.buildEvents(prj)
227		local function output(name, steps)
228			if #steps > 0 then
229				steps = os.translateCommandsAndPaths(steps, prj.basedir, prj.location)
230				steps = table.implode(steps, "", "", "\r\n")
231				_x(2,'<%sBuildEvent>%s</%sBuildEvent>', name, steps, name)
232			end
233		end
234
235		local cfg = project.getfirstconfig(prj)
236		if #cfg.prebuildcommands > 0 or #cfg.postbuildcommands > 0 then
237			_p(1,'<PropertyGroup>')
238			output("Pre", cfg.prebuildcommands)
239			output("Post", cfg.postbuildcommands)
240			_p(1,'</PropertyGroup>')
241		end
242	end
243
244
245--
246-- Write the compiler flags for a particular configuration.
247--
248
249	function dotnetbase.compilerProps(cfg)
250		_x(2,'<DefineConstants>%s</DefineConstants>', table.concat(cfg.defines, ";"))
251
252		_p(2,'<ErrorReport>prompt</ErrorReport>')
253		_p(2,'<WarningLevel>4</WarningLevel>')
254
255		if not dotnetbase.isNewFormatProject(cfg) then
256			dotnetbase.allowUnsafeBlocks(cfg)
257		end
258
259		if cfg.flags.FatalCompileWarnings then
260			_p(2,'<TreatWarningsAsErrors>true</TreatWarningsAsErrors>')
261		end
262
263		dotnetbase.debugCommandParameters(cfg)
264	end
265
266--
267-- Write out the debug start parameters for MonoDevelop/Xamarin Studio.
268--
269
270	function dotnetbase.debugCommandParameters(cfg)
271		if #cfg.debugargs > 0 then
272			_x(2,'<Commandlineparameters>%s</Commandlineparameters>', table.concat(cfg.debugargs, " "))
273		end
274	end
275
276--
277-- Write out the debugging and optimization flags for a configuration.
278--
279
280	function dotnetbase.debugProps(cfg)
281		if cfg.symbols == p.ON then
282			_p(2,'<DebugSymbols>true</DebugSymbols>')
283			_p(2,'<DebugType>full</DebugType>')
284		else
285			_p(2,'<DebugType>pdbonly</DebugType>')
286		end
287		_p(2,'<Optimize>%s</Optimize>', iif(config.isOptimizedBuild(cfg), "true", "false"))
288	end
289
290
291--
292-- Write out the target and intermediates settings for a configuration.
293--
294
295	function dotnetbase.outputProps(cfg)
296		local outdir = vstudio.path(cfg, cfg.buildtarget.directory)
297		_x(2,'<OutputPath>%s\\</OutputPath>', outdir)
298
299		-- Want to set BaseIntermediateOutputPath because otherwise VS will create obj/
300		-- anyway. But VS2008 throws up ominous warning if present.
301		local objdir = vstudio.path(cfg, cfg.objdir)
302		if _ACTION > "vs2008" and not dotnetbase.isNewFormatProject(cfg) then
303			_x(2,'<BaseIntermediateOutputPath>%s\\</BaseIntermediateOutputPath>', objdir)
304			_p(2,'<IntermediateOutputPath>$(BaseIntermediateOutputPath)</IntermediateOutputPath>')
305		else
306			_x(2,'<IntermediateOutputPath>%s\\</IntermediateOutputPath>', objdir)
307		end
308	end
309
310
311--
312-- Write out the references item group.
313--
314
315	dotnetbase.elements.references = function(prj)
316		return {
317			dotnetbase.assemblyReferences,
318			dotnetbase.nuGetReferences,
319		}
320	end
321
322	function dotnetbase.references(prj)
323		_p(1,'<ItemGroup>')
324		p.callArray(dotnetbase.elements.references, prj)
325		_p(1,'</ItemGroup>')
326	end
327
328
329--
330-- Write the list of assembly (system, or non-sibling) references.
331--
332
333	function dotnetbase.assemblyReferences(prj)
334		-- C# doesn't support per-configuration links (does it?) so just use
335		-- the settings from the first available config instead
336		local cfg = project.getfirstconfig(prj)
337
338		config.getlinks(cfg, "system", function(original, decorated)
339			local name = path.getname(decorated)
340			if path.getextension(name) == ".dll" then
341				name = name.sub(name, 1, -5)
342			end
343
344			if decorated:find("/", nil, true) then
345				_x(2,'<Reference Include="%s">', name)
346				local decPath, decVars = decorated:match("(.-),")
347				if not decPath then
348					decPath = decorated
349				end
350
351				_x(3,'<HintPath>%s</HintPath>', path.appendextension(path.translate(decPath), ".dll"))
352
353				if not config.isCopyLocal(prj, original, true) then
354					_p(3,"<Private>False</Private>")
355				end
356
357				_p(2,'</Reference>')
358			else
359				_x(2,'<Reference Include="%s" />', name)
360			end
361		end)
362	end
363
364
365--
366-- This is a bit janky. To compare versions, we extract all numbers from the
367-- given string and right-pad the result with zeros. Then we can just do a
368-- lexicographical compare on the resulting strings.
369--
370-- This is so that we can compare version strings such as "4.6" and "net451"
371-- with each other.
372--
373
374	function dotnetbase.makeVersionComparable(version)
375		local numbers = ""
376
377		for number in version:gmatch("%d") do
378			numbers = numbers .. number
379		end
380
381		return string.format("%-10d", numbers):gsub(" ", "0")
382	end
383
384
385--
386-- https://github.com/NuGet/NuGet.Client/blob/dev/test/NuGet.Core.Tests/NuGet.Frameworks.Test/NuGetFrameworkParseTests.cs
387--
388
389	function dotnetbase.frameworkVersionForFolder(folder)
390		-- If this exporter ever supports frameworks such as "netstandard1.3",
391		-- "sl4", "sl5", "uap10", "wp8" or "wp71", this code will need changing
392		-- to match the right folders, depending on the current framework.
393
394		-- Right now this only matches folders for the .NET Framework.
395
396		if folder:match("^net%d+$") or folder:match("^[0-9%.]+$") then
397			return dotnetbase.makeVersionComparable(folder)
398		elseif folder == "net" then
399			return dotnetbase.makeVersionComparable("0")
400		end
401	end
402
403
404--
405-- Write the list of NuGet references.
406--
407
408	function dotnetbase.nuGetReferences(prj)
409		if _ACTION >= "vs2010" and not vstudio.nuget2010.supportsPackageReferences(prj) then
410			for _, package in ipairs(prj.nuget) do
411				local id = vstudio.nuget2010.packageId(package)
412				local packageAPIInfo = vstudio.nuget2010.packageAPIInfo(prj, package)
413
414				local cfg = p.project.getfirstconfig(prj)
415				local action = p.action.current()
416				local targetFramework = cfg.dotnetframework or action.vstudio.targetFramework
417
418				local targetVersion = dotnetbase.makeVersionComparable(targetFramework)
419
420				-- Figure out what folder contains the files for the nearest
421				-- supported .NET Framework version.
422
423				local files = {}
424
425				local bestVersion, bestFolder
426
427				for _, file in ipairs(packageAPIInfo.packageEntries) do
428					local folder = file:match("^lib[\\/](.+)[\\/]")
429
430					if folder and path.hasextension(file, ".dll") then
431						local version = dotnetbase.frameworkVersionForFolder(folder)
432
433						if version then
434							files[folder] = files[folder] or {}
435							table.insert(files[folder], file)
436
437							if version <= targetVersion and (not bestVersion or version > bestVersion) then
438								bestVersion = version
439								bestFolder = folder
440							end
441						end
442					end
443				end
444
445				if not bestVersion then
446					p.error("NuGet package '%s' is not compatible with project '%s' .NET Framework version '%s'", id, prj.name, targetFramework)
447				end
448
449				-- Now, add references for all DLLs in that folder.
450
451				for _, file in ipairs(files[bestFolder]) do
452					-- There's some stuff missing from this include that we
453					-- can't get from the API and would need to download and
454					-- extract the package to figure out. It looks like we can
455					-- just omit it though.
456					--
457					-- So, for example, instead of:
458					--
459					-- <Reference Include="nunit.framework, Version=3.6.1.0,
460					-- <Culture=neutral, PublicKeyToken=2638cd05610744eb,
461					-- <processorArchitecture=MSIL">
462					--
463					-- We're just outputting:
464					--
465					-- <Reference Include="nunit.framework">
466
467					_x(2, '<Reference Include="%s">', path.getbasename(file))
468					_x(3, '<HintPath>%s</HintPath>', vstudio.path(prj, p.filename(prj.workspace, string.format("packages\\%s.%s\\%s", id, packageAPIInfo.verbatimVersion or packageAPIInfo.version, file))))
469
470					if config.isCopyLocal(prj, package, true) then
471						_p(3, '<Private>True</Private>')
472					else
473						_p(3, '<Private>False</Private>')
474					end
475
476					_p(2, '</Reference>')
477				end
478			end
479		end
480	end
481
482
483--
484-- Write the list of project dependencies.
485--
486	function dotnetbase.projectReferences(prj)
487		if not dotnetbase.isNewFormatProject(prj) then
488			_p(1,'<ItemGroup>')
489		end
490
491		local deps = project.getdependencies(prj, 'linkOnly')
492		if #deps > 0 then
493			if dotnetbase.isNewFormatProject(prj) then
494				_p(1,'<ItemGroup>')
495			end
496
497			for _, dep in ipairs(deps) do
498				local relpath = vstudio.path(prj, vstudio.projectfile(dep))
499				_x(2,'<ProjectReference Include="%s">', relpath)
500				_p(3,'<Project>{%s}</Project>', dep.uuid)
501				_x(3,'<Name>%s</Name>', dep.name)
502
503				if not config.isCopyLocal(prj, dep.name, true) then
504					_p(3,"<Private>False</Private>")
505				end
506
507				_p(2,'</ProjectReference>')
508			end
509
510			if dotnetbase.isNewFormatProject(prj) then
511				_p(1,'</ItemGroup>')
512			end
513		end
514
515		if not dotnetbase.isNewFormatProject(prj) then
516			_p(1,'</ItemGroup>')
517		end
518	end
519
520--
521-- Write the list of package dependencies.
522--
523	function dotnetbase.packageReferences(prj)
524		if vstudio.nuget2010.supportsPackageReferences(prj) then
525			local hasNuget = prj.nuget and #prj.nuget>0
526			for cfg in project.eachconfig(prj) do
527				if cfg.nuget and #cfg.nuget>0 then
528					hasNuget = true
529				end
530			end
531			if hasNuget then
532				_p(1,'<ItemGroup>')
533				if prj.nuget and #prj.nuget>0 then
534					for _, package in ipairs(prj.nuget) do
535						_p(2,'<PackageReference Include="%s" Version="%s"/>', vstudio.nuget2010.packageId(package), vstudio.nuget2010.packageVersion(package))
536					end
537				end
538				for cfg in project.eachconfig(prj) do
539					if cfg.nuget and #cfg.nuget>0 then
540						for _, package in ipairs(cfg.nuget) do
541							if prj.nuget[package]==nil then
542								_p(2,'<PackageReference Include="%s" Version="%s" %s/>', vstudio.nuget2010.packageId(package), vstudio.nuget2010.packageVersion(package), dotnetbase.condition(cfg))
543							end
544						end
545					end
546				end
547				_p(1,'</ItemGroup>')
548			end
549		end
550	end
551
552--
553-- Return the Visual Studio architecture identification string. The logic
554-- to select this is getting more complicated in VS2010, but I haven't
555-- tackled all the permutations yet.
556--
557
558	function dotnetbase.arch(cfg)
559		local arch = vstudio.archFromConfig(cfg)
560		if arch == "Any CPU" then
561			arch = "AnyCPU"
562		end
563		return arch
564	end
565
566
567--
568-- Write the PropertyGroup element for a specific configuration block.
569--
570
571	function dotnetbase.propertyGroup(cfg)
572		p.push('<PropertyGroup %s>', dotnetbase.condition(cfg))
573
574		local arch = dotnetbase.arch(cfg)
575		if arch ~= "AnyCPU" or _ACTION > "vs2008" then
576			p.x('<PlatformTarget>%s</PlatformTarget>', arch)
577		end
578	end
579
580
581--
582-- Generators for individual project elements.
583--
584
585	function dotnetbase.applicationIcon(prj)
586		if prj.icon then
587			local icon = vstudio.path(prj, prj.icon)
588			_p(1,'<PropertyGroup>')
589			_x(2,'<ApplicationIcon>%s</ApplicationIcon>', icon)
590			_p(1,'</PropertyGroup>')
591		end
592	end
593
594---------------------------------------------------------------------------
595--
596-- Support functions
597--
598---------------------------------------------------------------------------
599
600--
601-- Format and return a Visual Studio Condition attribute.
602--
603
604	function dotnetbase.condition(cfg)
605		local platform = vstudio.projectPlatform(cfg)
606		local arch = dotnetbase.arch(cfg)
607		return string.format('Condition=" \'$(Configuration)|$(Platform)\' == \'%s|%s\' "', platform, arch)
608	end
609
610
611--
612-- When given a .NET Framework version, returns it formatted for NuGet.
613--
614
615	function dotnetbase.formatNuGetFrameworkVersion(framework)
616		return "net" .. framework:gsub("%.", "")
617	end
618
619---------------------------------------------------------------------------
620--
621-- Handlers for individual project elements
622--
623---------------------------------------------------------------------------
624
625	function dotnetbase.appDesignerFolder(cfg)
626		_p(2,'<AppDesignerFolder>Properties</AppDesignerFolder>')
627	end
628
629
630	function dotnetbase.assemblyName(cfg)
631		if not dotnetbase.isNewFormatProject(cfg) --[[or cfg.assemblyname]] then
632			_p(2,'<AssemblyName>%s</AssemblyName>', cfg.buildtarget.basename)
633		end
634	end
635
636
637	function dotnetbase.commonProperties(prj)
638		if _ACTION > "vs2010" then
639			_p(1,'<Import Project="$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props" Condition="Exists(\'$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\')" />')
640		end
641	end
642
643
644	function dotnetbase.configurationCondition(cfg)
645		_x(2,'<Configuration Condition=" \'$(Configuration)\' == \'\' ">%s</Configuration>', cfg.buildcfg)
646	end
647
648
649	function dotnetbase.fileAlignment(cfg)
650		if _ACTION >= "vs2010" and not dotnetbase.isNewFormatProject(cfg) then
651			_p(2,'<FileAlignment>512</FileAlignment>')
652		end
653	end
654
655
656	function dotnetbase.bindingRedirects(cfg)
657		if _ACTION >= "vs2015" and not dotnetbase.isNewFormatProject(cfg) then
658			_p(2, '<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>')
659		end
660	end
661
662
663	function dotnetbase.outputType(cfg)
664		_p(2,'<OutputType>%s</OutputType>', dotnet.getkind(cfg))
665	end
666
667
668	function dotnetbase.platformCondition(cfg)
669		_p(2,'<Platform Condition=" \'$(Platform)\' == \'\' ">%s</Platform>', dotnetbase.arch(cfg.project))
670	end
671
672
673	function dotnetbase.productVersion(cfg)
674		local action = p.action.current()
675		if action.vstudio.productVersion then
676			_p(2,'<ProductVersion>%s</ProductVersion>', action.vstudio.productVersion)
677		end
678	end
679
680	function dotnetbase.projectGuid(cfg)
681		_p(2,'<ProjectGuid>{%s}</ProjectGuid>', cfg.uuid)
682	end
683
684
685	function dotnetbase.projectTypeGuids(cfg)
686		if cfg.flags.WPF then
687			_p(2,'<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>')
688		end
689	end
690
691
692	function dotnetbase.rootNamespace(cfg)
693		if not dotnetbase.isNewFormatProject(cfg) or cfg.namespace then
694			_p(2,'<RootNamespace>%s</RootNamespace>', cfg.namespace or cfg.buildtarget.basename)
695		end
696	end
697
698
699	function dotnetbase.schemaVersion(cfg)
700		local action = p.action.current()
701		if action.vstudio.csprojSchemaVersion then
702			_p(2,'<SchemaVersion>%s</SchemaVersion>', action.vstudio.csprojSchemaVersion)
703		end
704	end
705
706
707	function dotnetbase.NoWarn(cfg)
708		if #cfg.disablewarnings > 0 then
709			local warnings = table.concat(cfg.disablewarnings, ";")
710			_p(2,'<NoWarn>%s</NoWarn>', warnings)
711		end
712	end
713
714	function dotnetbase.targetFrameworkVersion(cfg)
715		local action = p.action.current()
716		local framework = cfg.dotnetframework or action.vstudio.targetFramework
717		if framework and not dotnetbase.isNewFormatProject(cfg) then
718			_p(2,'<TargetFrameworkVersion>v%s</TargetFrameworkVersion>', framework)
719		end
720	end
721
722	function dotnetbase.csversion(cfg)
723		if cfg.csversion then
724			_p(2,'<LangVersion>%s</LangVersion>', cfg.csversion)
725		end
726	end
727
728	function dotnetbase.targetFrameworkProfile(cfg)
729		if _ACTION == "vs2010" then
730			_p(2,'<TargetFrameworkProfile>')
731			_p(2,'</TargetFrameworkProfile>')
732		end
733	end
734
735
736	function dotnetbase.xmlDeclaration()
737		if _ACTION > "vs2008" then
738			p.xmlUtf8()
739		end
740	end
741
742	function dotnetbase.isNewFormatProject(cfg)
743		local framework = cfg.dotnetframework
744		if not framework then
745			return false
746		end
747
748		if framework:find('^net') ~= nil then
749			return true
750		end
751
752		return false
753	end
754
755	function dotnetbase.netcore.targetFramework(cfg)
756		local action = p.action.current()
757		local framework = cfg.dotnetframework or action.vstudio.targetFramework
758		if framework and dotnetbase.isNewFormatProject(cfg) then
759			_p(2,'<TargetFramework>%s</TargetFramework>', framework)
760		end
761	end
762
763	function dotnetbase.netcore.enableDefaultCompileItems(cfg)
764		_p(2,'<EnableDefaultCompileItems>%s</EnableDefaultCompileItems>', iif(cfg.enableDefaultCompileItems, "true", "false"))
765	end
766
767	function dotnetbase.netcore.useWpf(cfg)
768		if cfg.flags.WPF then
769			_p(2,'<UseWpf>true</UseWpf>')
770		end
771	end
772
773	function dotnetbase.allowUnsafeBlocks(cfg)
774		if cfg.clr == "Unsafe" then
775			_p(2,'<AllowUnsafeBlocks>true</AllowUnsafeBlocks>')
776		end
777	end