Fix issue #200: Prevent generation of empty Visual Studio .user files

This commit is contained in:
Jason Perkins 2015-03-12 11:28:08 -04:00
commit 5d9829a1da
14 changed files with 405 additions and 131 deletions

View File

@ -13,3 +13,4 @@ Since 5.0-alpha1:
* Action arguments (_ARGS) are now keyed by both index and value
* Configuration baking and validation now skipped for execute only actions
* os.findlib() now accepts paths to search as argument
* Visual Studio .user files are now only generated if not empty

View File

@ -1,7 +1,7 @@
--
-- actions/vstudio/vs2005.lua
-- Add support for the Visual Studio 2005 project formats.
-- Copyright (c) 2008-2014 Jason Perkins and the Premake project
-- Copyright (c) 2008-2015 Jason Perkins and the Premake project
--
premake.vstudio.vs2005 = {}
@ -31,10 +31,22 @@
if premake.project.isdotnet(prj) then
premake.generate(prj, ".csproj", vstudio.cs2005.generate)
premake.generate(prj, ".csproj.user", vstudio.cs2005.generate_user)
-- Skip generation of empty user files
local user = p.capture(function() vstudio.cs2005.generateUser(prj) end)
if #user > 0 then
p.generate(prj, ".csproj.user", function() p.outln(user) end)
end
elseif premake.project.iscpp(prj) then
premake.generate(prj, ".vcproj", vstudio.vc200x.generate)
premake.generate(prj, ".vcproj.user", vstudio.vc200x.generate_user)
-- Skip generation of empty user files
local user = p.capture(function() vstudio.vc200x.generateUser(prj) end)
if #user > 0 then
p.generate(prj, ".vcproj.user", function() p.outln(user) end)
end
end
end

View File

@ -1,52 +1,90 @@
--
-- vs2005_csproj_user.lua
-- Generate a Visual Studio 2005/2008 C# .user file.
-- Copyright (c) 2009-2014 Jason Perkins and the Premake project
-- Copyright (c) 2009-2015 Jason Perkins and the Premake project
--
local p = premake
local cs2005 = p.vstudio.cs2005
local project = p.project
local m = p.vstudio.cs2005
--
-- Generate a Visual Studio 200x C# user file, with support for the new platforms API.
-- Generate a Visual Studio 200x C# user file.
--
function cs2005.generate_user(prj)
p.vstudio.projectElement()
_p(1,'<PropertyGroup>')
m.elements.userProjectPropertyGroup = function()
return {
m.referencePath,
}
end
-- Per-configuration reference paths aren't supported (are they?) so just
-- use the first configuration in the project
local cfg = project.getfirstconfig(prj)
m.elements.userConfigPropertyGroup = function()
return {
m.localDebuggerCommandArguments,
}
end
local refpaths = path.translate(project.getrelative(prj, cfg.libdirs))
_p(2,'<ReferencePath>%s</ReferencePath>', table.concat(refpaths, ";"))
_p(' </PropertyGroup>')
for cfg in project.eachconfig(prj) do
local contents = p.capture(function()
cs2005.debugsettings(cfg)
function m.generateUser(prj)
-- Only want output if there is something to configure
local prjGroup = p.capture(function()
p.push(2)
p.callArray(m.elements.userProjectPropertyGroup, prj)
p.pop(2)
end)
if #contents > 0 then
_p(1,'<PropertyGroup %s>', cs2005.condition(cfg))
p.outln(contents)
_p(1,'</PropertyGroup>')
local contents = {}
local size = 0
for cfg in p.project.eachconfig(prj) do
contents[cfg] = p.capture(function()
p.push(2)
p.callArray(m.elements.userConfigPropertyGroup, cfg)
p.pop(2)
end)
size = size + #contents[cfg]
end
if #prjGroup > 0 or size > 0 then
p.vstudio.projectElement()
if #prjGroup > 0 then
p.push('<PropertyGroup>')
p.outln(prjGroup)
p.pop('</PropertyGroup>')
end
for cfg in p.project.eachconfig(prj) do
if #contents[cfg] > 0 then
p.push('<PropertyGroup %s>', m.condition(cfg))
p.outln(contents[cfg])
p.pop('</PropertyGroup>')
end
end
_p('</Project>')
p.pop('</Project>')
end
end
function cs2005.debugsettings(cfg)
cs2005.localDebuggerCommandArguments(cfg)
---
-- Output any reference paths required by the project.
---
function m.referencePath(prj)
-- Per-configuration reference paths aren't supported (are they?) so just
-- use the first configuration in the project
local cfg = p.project.getfirstconfig(prj)
local paths = path.translate(p.project.getrelative(prj, cfg.libdirs))
if #paths > 0 then
p.w('<ReferencePath>%s</ReferencePath>', table.concat(paths, ";"))
end
end
function cs2005.localDebuggerCommandArguments(cfg)
function m.localDebuggerCommandArguments(cfg)
if #cfg.debugargs > 0 then
_x(2,'<StartArguments>%s</StartArguments>', table.concat(cfg.debugargs, " "))
p.x('<StartArguments>%s</StartArguments>', table.concat(cfg.debugargs, " "))
end
end

View File

@ -1,38 +1,72 @@
--
-- vs200x_vcproj_user.lua
-- Generate a Visual Studio 2002-2008 C/C++ project .user file
-- Copyright (c) 2011-2013 Jason Perkins and the Premake project
-- Copyright (c) 2011-2015 Jason Perkins and the Premake project
--
local p = premake
local vstudio = premake.vstudio
local project = p.project
local m = p.vstudio.vc200x
--
-- Generate a Visual Studio 200x C++ user file, with support for the new platforms API.
--
function m.generate_user(prj)
p.indent("\t")
m.xmlElement()
p.push('<VisualStudioUserFile')
p.w('ProjectType="Visual C++"')
m.version()
p.w('ShowAllFiles="false"')
p.w('>')
m.elements.user = function(cfg)
return {
m.debugSettings,
}
end
function m.generateUser(prj)
p.indent("\t")
-- Only want output if there is something to configure
local contents = {}
local size = 0
for cfg in p.project.eachconfig(prj) do
contents[cfg] = p.capture(function()
p.push(4)
p.callArray(m.elements.user, cfg)
p.pop(4)
end)
size = size + #contents[cfg]
end
if size > 0 then
m.xmlElement()
m.visualStudioUserFile()
p.push('<Configurations>')
for cfg in project.eachconfig(prj) do
m.userconfiguration(cfg)
m.debugdir(cfg)
for cfg in p.project.eachconfig(prj) do
m.userConfiguration(cfg)
p.push('<DebugSettings')
if #contents[cfg] > 0 then
p.outln(contents[cfg])
end
p.pop('/>')
p.pop('</Configuration>')
end
p.pop('</Configurations>')
p.pop('</VisualStudioUserFile>')
end
end
---
-- Output the opening project tag.
---
function m.visualStudioUserFile()
p.push('<VisualStudioUserFile')
p.w('ProjectType="Visual C++"')
m.version()
p.w('ShowAllFiles="false"')
p.w('>')
end
--
@ -40,40 +74,60 @@
-- build configuration/platform pairing.
--
function m.userconfiguration(cfg)
function m.userConfiguration(cfg)
p.push('<Configuration')
p.x('Name="%s"', vstudio.projectConfig(cfg))
p.x('Name="%s"', p.vstudio.projectConfig(cfg))
p.w('>')
end
--
-- Write out the debug settings for this project.
--
function m.debugdir(cfg)
p.push('<DebugSettings')
if cfg.debugcommand then
local command = project.getrelative(cfg.project, cfg.debugcommand)
p.x('Command="%s"', path.translate(command))
m.elements.debugSettings = function(cfg)
return {
m.debugCommand,
m.debugDir,
m.debugArgs,
m.debugEnvironment,
}
end
if cfg.debugdir then
local debugdir = project.getrelative(cfg.project, cfg.debugdir)
p.x('WorkingDirectory="%s"', path.translate(debugdir))
function m.debugSettings(cfg)
p.callArray(m.elements.debugSettings, cfg)
end
function m.debugArgs(cfg)
if #cfg.debugargs > 0 then
p.x('CommandArguments="%s"', table.concat(cfg.debugargs, " "))
end
end
function m.debugCommand(cfg)
if cfg.debugcommand then
local command = p.project.getrelative(cfg.project, cfg.debugcommand)
p.x('Command="%s"', path.translate(command))
end
end
function m.debugDir(cfg)
if cfg.debugdir then
local debugdir = p.project.getrelative(cfg.project, cfg.debugdir)
p.x('WorkingDirectory="%s"', path.translate(debugdir))
end
end
function m.debugEnvironment(cfg)
if #cfg.debugenvs > 0 then
p.x('Environment="%s"', table.concat(cfg.debugenvs, "\n"))
if cfg.flags.DebugEnvsDontMerge then
p.x('EnvironmentMerge="false"')
end
end
p.pop('/>')
end

View File

@ -1,7 +1,7 @@
--
-- actions/vstudio/vs2010.lua
-- Add support for the Visual Studio 2010 project formats.
-- Copyright (c) 2009-2014 Jason Perkins and the Premake project
-- Copyright (c) 2009-2015 Jason Perkins and the Premake project
--
premake.vstudio.vs2010 = {}
@ -40,15 +40,27 @@
if premake.project.isdotnet(prj) then
premake.generate(prj, ".csproj", vstudio.cs2005.generate)
premake.generate(prj, ".csproj.user", vstudio.cs2005.generate_user)
-- Skip generation of empty user files
local user = p.capture(function() vstudio.cs2005.generateUser(prj) end)
if #user > 0 then
p.generate(prj, ".csproj.user", function() p.outln(user) end)
end
elseif premake.project.iscpp(prj) then
premake.generate(prj, ".vcxproj", vstudio.vc2010.generate)
premake.generate(prj, ".vcxproj.user", vstudio.vc2010.generateUser)
-- Skip generation of empty user files
local user = p.capture(function() vstudio.vc2010.generateUser(prj) end)
if #user > 0 then
p.generate(prj, ".vcxproj.user", function() p.outln(user) end)
end
-- Only generate a filters file if the source tree actually has subfolders
if tree.hasbranches(project.getsourcetree(prj)) then
premake.generate(prj, ".vcxproj.filters", vstudio.vc2010.generateFilters)
end
end
end

View File

@ -1,36 +1,55 @@
--
-- vs2010_vcxproj_user.lua
-- Generate a Visual Studio 201x C/C++ project .user file
-- Copyright (c) 2011-2013 Jason Perkins and the Premake project
-- Copyright (c) 2011-2015 Jason Perkins and the Premake project
--
local p = premake
local vstudio = p.vstudio
local vc2010 = p.vstudio.vc2010
local project = p.project
local m = p.vstudio.vc2010
--
-- Generate a Visual Studio 201x C++ user file, with support for the new platforms API.
-- Generate a Visual Studio 201x C++ user file.
--
m.elements.user = function(cfg)
return {
m.debugSettings,
}
end
function m.generateUser(prj)
-- Only want output if there is something to configure
local contents = {}
local size = 0
for cfg in p.project.eachconfig(prj) do
contents[cfg] = p.capture(function()
p.push(2)
p.callArray(m.elements.user, cfg)
p.pop(2)
end)
size = size + #contents[cfg]
end
if size > 0 then
m.xmlDeclaration()
m.userProject()
for cfg in project.eachconfig(prj) do
for cfg in p.project.eachconfig(prj) do
p.push('<PropertyGroup %s>', m.condition(cfg))
m.debugSettings(cfg)
if #contents[cfg] > 0 then
p.outln(contents[cfg])
end
p.pop('</PropertyGroup>')
end
_p('</Project>')
p.pop('</Project>')
end
end
--
-- Output the XML declaration and opening <Project> tag.
-- Output the opening <Project> tag.
--
function m.userProject()
@ -41,24 +60,24 @@
vc2010.elements.debugSettings = function(cfg)
m.elements.debugSettings = function(cfg)
return {
vc2010.localDebuggerCommand,
vc2010.localDebuggerWorkingDirectory,
vc2010.debuggerFlavor,
vc2010.localDebuggerCommandArguments,
vc2010.localDebuggerEnvironment,
vc2010.localDebuggerMergeEnvironment,
m.localDebuggerCommand,
m.localDebuggerWorkingDirectory,
m.debuggerFlavor,
m.localDebuggerCommandArguments,
m.localDebuggerEnvironment,
m.localDebuggerMergeEnvironment,
}
end
function vc2010.debugSettings(cfg)
p.callArray(vc2010.elements.debugSettings, cfg)
function m.debugSettings(cfg)
p.callArray(m.elements.debugSettings, cfg)
end
function vc2010.debuggerFlavor(cfg)
function m.debuggerFlavor(cfg)
if cfg.debugdir or cfg.debugcommand then
p.w('<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>')
end
@ -66,16 +85,16 @@
function vc2010.localDebuggerCommand(cfg)
function m.localDebuggerCommand(cfg)
if cfg.debugcommand then
local dir = project.getrelative(cfg.project, cfg.debugcommand)
local dir = p.project.getrelative(cfg.project, cfg.debugcommand)
p.w('<LocalDebuggerCommand>%s</LocalDebuggerCommand>', path.translate(dir))
end
end
function vc2010.localDebuggerCommandArguments(cfg)
function m.localDebuggerCommandArguments(cfg)
if #cfg.debugargs > 0 then
p.x('<LocalDebuggerCommandArguments>%s</LocalDebuggerCommandArguments>', table.concat(cfg.debugargs, " "))
end
@ -83,16 +102,16 @@
function vc2010.localDebuggerWorkingDirectory(cfg)
function m.localDebuggerWorkingDirectory(cfg)
if cfg.debugdir then
local dir = project.getrelative(cfg.project, cfg.debugdir)
local dir = p.project.getrelative(cfg.project, cfg.debugdir)
p.x('<LocalDebuggerWorkingDirectory>%s</LocalDebuggerWorkingDirectory>', path.translate(dir))
end
end
function vc2010.localDebuggerEnvironment(cfg)
function m.localDebuggerEnvironment(cfg)
if #cfg.debugenvs > 0 then
local envs = table.concat(cfg.debugenvs, "\n")
if cfg.flags.DebugEnvsInherit then
@ -108,7 +127,7 @@
function vc2010.localDebuggerMergeEnvironment(cfg)
function m.localDebuggerMergeEnvironment(cfg)
if #cfg.debugenvs > 0 and cfg.flags.DebugEnvsDontMerge then
p.w(2,'<LocalDebuggerMergeEnvironment>false</LocalDebuggerMergeEnvironment>')
end

View File

@ -164,6 +164,7 @@
_indentLevel = 0
callback(obj)
f:close()
_indentLevel = 0
end

View File

@ -55,7 +55,7 @@ return {
"tools/test_gcc.lua",
"tools/test_msc.lua",
-- Visual Studio 2005-2010 C# projects
-- Visual Studio 2005-2013 C# projects
"actions/vstudio/cs2005/test_assembly_refs.lua",
"actions/vstudio/cs2005/test_build_events.lua",
"actions/vstudio/cs2005/test_common_props.lua",
@ -69,8 +69,9 @@ return {
"actions/vstudio/cs2005/test_project_refs.lua",
"actions/vstudio/cs2005/projectsettings.lua",
"actions/vstudio/cs2005/test_targets.lua",
"actions/vstudio/cs2005/test_user_file.lua",
-- Visual Studio 2005-2010 solutions
-- Visual Studio 2005-2013 solutions
"actions/vstudio/sln2005/test_dependencies.lua",
"actions/vstudio/sln2005/test_header.lua",
"actions/vstudio/sln2005/test_nested_projects.lua",
@ -92,8 +93,9 @@ return {
"actions/vstudio/vc200x/test_project.lua",
"actions/vstudio/vc200x/test_project_refs.lua",
"actions/vstudio/vc200x/test_resource_compiler.lua",
"actions/vstudio/vc200x/test_user_file.lua",
-- Visual Studio 2010 C/C++ projects
-- Visual Studio 2010-2013 C/C++ projects
"actions/vstudio/vc2010/test_assembly_refs.lua",
"actions/vstudio/vc2010/test_build_events.lua",
"actions/vstudio/vc2010/test_compile_settings.lua",
@ -118,6 +120,7 @@ return {
"actions/vstudio/vc2010/test_prop_sheet.lua",
"actions/vstudio/vc2010/test_resource_compile.lua",
"actions/vstudio/vc2010/test_rule_vars.lua",
"actions/vstudio/vc2010/test_user_file.lua",
-- Makefile tests
"actions/make/test_make_escaping.lua",

View File

@ -65,12 +65,10 @@
function suite.debugStartArguments()
debugargs "foobar"
local cfg = test.getconfig(prj, "Debug")
cs2005.debugsettings(cfg)
cs2005.localDebuggerCommandArguments(cfg)
test.capture [[
<StartArguments>foobar</StartArguments>
<StartArguments>foobar</StartArguments>
]]
end

View File

@ -0,0 +1,49 @@
--
-- tests/actions/vstudio/cs2005/test_user_file.lua
-- Verify handling of empty and non-empty .user files for VC#.
-- Copyright (c) 2015 Jason Perkins and the Premake project
--
local suite = test.declare("vstudio_cs2005_user_file")
local cs2005 = premake.vstudio.cs2005
--
-- Setup
--
local sln
function suite.setup()
_ACTION = "vs2008"
sln = test.createsolution()
language "C#"
end
local function prepare()
local prj = test.getproject(sln, 1)
cs2005.generateUser(prj)
end
--
-- If no debugger settings have been specified, then the .user
-- file should not be written at all.
--
function suite.noOutput_onNoSettings()
prepare()
test.isemptycapture()
end
--
-- If a debugger setting has been specified, output.
--
function suite.doesOutput_onDebugSettings()
debugargs { "hello" }
prepare()
test.hasoutput()
end

View File

@ -22,7 +22,7 @@
local function prepare()
local cfg = test.getconfig(prj, "Debug")
vc200x.debugdir(cfg)
vc200x.debugSettings(cfg)
end
@ -32,10 +32,7 @@
function suite.emptyBlock_onNoSettings()
prepare()
test.capture [[
<DebugSettings
/>
]]
test.isemptycapture()
end
@ -49,9 +46,7 @@
debugcommand "bin/emulator.exe"
prepare()
test.capture [[
<DebugSettings
Command="..\bin\emulator.exe"
/>
Command="..\bin\emulator.exe"
]]
end
@ -66,9 +61,7 @@
debugdir "bin/debug"
prepare()
test.capture [[
<DebugSettings
WorkingDirectory="..\bin\debug"
/>
WorkingDirectory="..\bin\debug"
]]
end
@ -81,9 +74,7 @@
debugargs { "arg1", "arg2" }
prepare()
test.capture [[
<DebugSettings
CommandArguments="arg1 arg2"
/>
CommandArguments="arg1 arg2"
]]
end
@ -96,9 +87,7 @@
debugenvs { "key=value" }
prepare()
test.capture [[
<DebugSettings
Environment="key=value"
/>
Environment="key=value"
]]
end
@ -111,9 +100,7 @@
debugenvs { 'key="value"' }
prepare()
test.capture [[
<DebugSettings
Environment="key=&quot;value&quot;"
/>
Environment="key=&quot;value&quot;"
]]
end
@ -127,9 +114,7 @@
debugenvs { "key=value", "foo=bar" }
prepare()
test.capture [[
<DebugSettings
Environment="key=value&#x0A;foo=bar"
/>
Environment="key=value&#x0A;foo=bar"
]]
end
@ -144,9 +129,7 @@
flags { "DebugEnvsDontMerge" }
prepare()
test.capture [[
<DebugSettings
Environment="key=value"
EnvironmentMerge="false"
/>
Environment="key=value"
EnvironmentMerge="false"
]]
end

View File

@ -0,0 +1,48 @@
--
-- tests/actions/vstudio/vc200x/test_user_file.lua
-- Verify handling of empty and non-empty .user files for VC'200x.
-- Copyright (c) 2015 Jason Perkins and the Premake project
--
local suite = test.declare("vstudio_vs200x_user_file")
local vc200x = premake.vstudio.vc200x
--
-- Setup
--
local sln
function suite.setup()
_ACTION = "vs2008"
sln = test.createsolution()
end
local function prepare()
local prj = test.getproject(sln, 1)
vc200x.generateUser(prj)
end
--
-- If no debugger settings have been specified, then the .user
-- file should not be written at all.
--
function suite.noOutput_onNoSettings()
prepare()
test.isemptycapture()
end
--
-- If a debugger setting has been specified, output.
--
function suite.doesOutput_onDebugSettings()
debugcommand "bin/emulator.exe"
prepare()
test.hasoutput()
end

View File

@ -0,0 +1,48 @@
--
-- tests/actions/vstudio/vc2010/test_user_file.lua
-- Verify handling of empty and non-empty .user files for VC'201x.
-- Copyright (c) 2015 Jason Perkins and the Premake project
--
local suite = test.declare("vstudio_vs2010_user_file")
local vc2010 = premake.vstudio.vc2010
--
-- Setup
--
local sln
function suite.setup()
_ACTION = "vs2010"
sln = test.createsolution()
end
local function prepare()
local prj = test.getproject(sln, 1)
vc2010.generateUser(prj)
end
--
-- If no debugger settings have been specified, then the .user
-- file should not be written at all.
--
function suite.noOutput_onNoSettings()
prepare()
test.isemptycapture()
end
--
-- If a debugger setting has been specified, output.
--
function suite.doesOutput_onDebugSettings()
debugcommand "bin/emulator.exe"
prepare()
test.hasoutput()
end

View File

@ -121,6 +121,14 @@
end
function test.hasoutput()
local actual = premake.captured()
if actual == "" then
test.fail("expected output, received none");
end
end
function test.isemptycapture()
local actual = premake.captured()
if actual ~= "" then