diff --git a/modules/vstudio/_manifest.lua b/modules/vstudio/_manifest.lua index f5efe5aa..bec75192 100644 --- a/modules/vstudio/_manifest.lua +++ b/modules/vstudio/_manifest.lua @@ -21,6 +21,7 @@ return { "vs2010_rules_xml.lua", "vs2012.lua", "vs2013.lua", + "vs2013_vcxitems.lua", "vs2015.lua", "vs2017.lua", "vs2019.lua" diff --git a/modules/vstudio/tests/_tests.lua b/modules/vstudio/tests/_tests.lua index c5b80af6..cd5f495b 100644 --- a/modules/vstudio/tests/_tests.lua +++ b/modules/vstudio/tests/_tests.lua @@ -31,6 +31,7 @@ return { "sln2005/test_projects.lua", "sln2005/test_platforms.lua", "sln2005/test_sections.lua", + "sln2005/test_shared_projects.lua", -- Visual Studio 2002-2008 C/C++ projects "vc200x/test_assembly_refs.lua", @@ -90,4 +91,7 @@ return { "vc2010/test_user_file.lua", "vc2010/test_vectorextensions.lua", "vc2010/test_ensure_nuget_imports.lua", + + -- Visual Studio 2013+ C/C++ Shared Items projects + "vc2013/test_vcxitems.lua", } diff --git a/modules/vstudio/tests/sln2005/test_platforms.lua b/modules/vstudio/tests/sln2005/test_platforms.lua index 3cf95ba4..a9d693b7 100644 --- a/modules/vstudio/tests/sln2005/test_platforms.lua +++ b/modules/vstudio/tests/sln2005/test_platforms.lua @@ -782,3 +782,23 @@ GlobalSection(SolutionConfigurationPlatforms) = preSolution EndGlobalSection ]] end + +--- +-- Check that when a shared items project is specified that no entries are added +-- to the project configuration platforms +--- + + function suite.onSharedItemsProject() + p.action.set("vs2013") + project "MyProject" + kind "SharedItems" + prepare() + test.capture [[ +GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 +EndGlobalSection +GlobalSection(ProjectConfigurationPlatforms) = postSolution +EndGlobalSection + ]] + end diff --git a/modules/vstudio/tests/sln2005/test_shared_projects.lua b/modules/vstudio/tests/sln2005/test_shared_projects.lua new file mode 100644 index 00000000..158f0adb --- /dev/null +++ b/modules/vstudio/tests/sln2005/test_shared_projects.lua @@ -0,0 +1,72 @@ +-- +-- tests/actions/vstudio/sln2005/test_shared_projects.lua +-- Validate generation of Visual Studio 2005+ SharedMSBuildProjectFiles entries. +-- Copyright (c) Jason Perkins and the Premake project +-- + + local p = premake + local suite = test.declare("vstudio_sln2005_shared_projects") + local sln2005 = p.vstudio.sln2005 + + +-- +-- Setup +-- + + local wks + + function suite.setup() + p.action.set("vs2013") + p.escaper(p.vstudio.vs2005.esc) + wks = workspace("MyWorkspace") + configurations { "Debug", "Release" } + language "C++" + end + + local function prepare() + sln2005.reorderProjects(wks) + sln2005.sharedProjects(wks) + end + +-- +-- Test the shared projects listing +-- + + function suite.noSharedProjects() + project "MyProject" + kind "ConsoleApp" + prepare() + + test.isemptycapture() + end + + + function suite.onSharedProjects() + project "MyProject" + kind "SharedItems" + prepare() + + test.capture [[ +GlobalSection(SharedMSBuildProjectFiles) = preSolution +MyProject.vcxitems*{42b5dbc6-ae1f-903d-f75d-41e363076e92}*SharedItemsImports = 9 +EndGlobalSection + ]] + end + + + function suite.onLinkedSharedProjects() + project "MyProject" + kind "SharedItems" + + project "MyProject2" + kind "ConsoleApp" + links { "MyProject" } + prepare() + + test.capture [[ +GlobalSection(SharedMSBuildProjectFiles) = preSolution +MyProject.vcxitems*{42b5dbc6-ae1f-903d-f75d-41e363076e92}*SharedItemsImports = 9 +MyProject.vcxitems*{b45d52a2-a015-94ef-091d-6d4bf5f32ee0}*SharedItemsImports = 4 +EndGlobalSection + ]] + end diff --git a/modules/vstudio/tests/vc2010/test_project_refs.lua b/modules/vstudio/tests/vc2010/test_project_refs.lua index 51ccca01..1609bba7 100644 --- a/modules/vstudio/tests/vc2010/test_project_refs.lua +++ b/modules/vstudio/tests/vc2010/test_project_refs.lua @@ -97,3 +97,20 @@ ]] end + + +-- +-- Shared items projects are referenced differently +-- + + function suite.sharedItemsProjects() + links { "MyProject" } + project("MyProject") + kind "SharedItems" + prepare() + test.capture [[ + + + + ]] + end diff --git a/modules/vstudio/tests/vc2013/test_vcxitems.lua b/modules/vstudio/tests/vc2013/test_vcxitems.lua new file mode 100644 index 00000000..80bc4a74 --- /dev/null +++ b/modules/vstudio/tests/vc2013/test_vcxitems.lua @@ -0,0 +1,109 @@ +-- +-- tests/actions/vstudio/vc2013/test_vcxitems.lua +-- Validate generation of the vcxitems project. +-- Copyright (c) Jason Perkins and the Premake project +-- + + local p = premake + local suite = test.declare("vstudio_vs2013_vcxitems") + local vc2013 = p.vstudio.vc2013 + + +-- +-- Setup +-- + + local wks, prj + + function suite.setup() + p.action.set("vs2013") + wks = test.createWorkspace() + end + + local function prepare() + kind "SharedItems" + prj = test.getproject(wks, 1) + end + + +-- +-- Check the structure with the default project values. +-- + + function suite.structureIsCorrect_onDefaultValues() + prepare() + vc2013.generate(prj) + test.capture [[ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + {42B5DBC6-AE1F-903D-F75D-41E363076E92} + + + + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + + + + + ]] + end + + +-- +-- Check the structure with files. +-- + + function suite.structureIsCorrect_onFiles() + files { "test.h", "test.cpp" } + prepare() + vc2013.generate(prj) + test.capture [[ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + {42B5DBC6-AE1F-903D-F75D-41E363076E92} + + + + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + + + + + + + + + + + ]] + end + + +-- +-- If the project name differs from the project filename, output a +-- element to indicate that. +-- + + function suite.projectName_OnFilename() + filename "MyProject_2013" + prepare() + vc2013.globals(prj) + test.capture [[ + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + {42B5DBC6-AE1F-903D-F75D-41E363076E92} + MyProject + + ]] + end diff --git a/modules/vstudio/vs2005_solution.lua b/modules/vstudio/vs2005_solution.lua index d818a581..740ea1be 100644 --- a/modules/vstudio/vs2005_solution.lua +++ b/modules/vstudio/vs2005_solution.lua @@ -101,6 +101,20 @@ end +-- +-- Build a relative path from the solution file to the project file +-- + + function sln2005.buildRelativePath(prj) + local prjpath = vstudio.projectfile(prj) + prjpath = vstudio.path(prj.workspace, prjpath) + + -- Unlike projects, solutions must use old-school %...% DOS style + -- for environment variables. + return prjpath:gsub("$%((.-)%)", "%%%1%%") + end + + -- -- Write out the list of projects and groups contained by the solution. -- @@ -110,16 +124,7 @@ tree.traverse(tr, { onleaf = function(n) local prj = n.project - - -- Build a relative path from the solution file to the project file - local prjpath = vstudio.projectfile(prj) - prjpath = vstudio.path(prj.workspace, prjpath) - - -- Unlike projects, solutions must use old-school %...% DOS style - -- for environment variables. - prjpath = prjpath:gsub("$%((.-)%)", "%%%1%%") - - p.x('Project("{%s}") = "%s", "%s", "{%s}"', vstudio.tool(prj), prj.name, prjpath, prj.uuid) + p.x('Project("{%s}") = "%s", "%s", "{%s}"', vstudio.tool(prj), prj.name, sln2005.buildRelativePath(prj), prj.uuid) p.push() sln2005.projectdependencies(prj) p.pop('EndProject') @@ -149,6 +154,41 @@ end +-- +-- Write out the list of shared project files and their links +-- + + function sln2005.sharedProjects(wks) + local contents = p.capture(function () + local tr = p.workspace.grouptree(wks) + p.tree.traverse(tr, { + onleaf = function(n) + local prj = n.project + + -- SharedItems projects reference their own UUID with a "9" + -- SharedItems projects reference the UUID of projects that link them with a "4" + if prj.kind == p.SHAREDITEMS then + p.w('%s*{%s}*SharedItemsImports = %s', sln2005.buildRelativePath(prj), prj.uuid:lower(), "9") + else + local deps = p.project.getdependencies(prj, 'linkOnly') + for _, dep in ipairs(deps) do + if dep.kind == p.SHAREDITEMS then + p.w('%s*{%s}*SharedItemsImports = %s', sln2005.buildRelativePath(dep), prj.uuid:lower(), "4") + end + end + end + end, + }) + end) + + if #contents > 0 then + p.push('GlobalSection(SharedMSBuildProjectFiles) = preSolution') + p.outln(contents) + p.pop('EndGlobalSection') + end + end + + -- -- Write out the list of project configuration platforms. -- @@ -167,6 +207,12 @@ tree.traverse(tr, { onleaf = function(n) local prj = n.project + + -- SharedItems projects don't have any configuration platform entries + if prj.kind == p.SHAREDITEMS then + return + end + table.foreachi(sorted, function(cfg) local context = {} -- Look up the matching project configuration. If none exist, this @@ -351,6 +397,7 @@ sln2005.elements.sections = function(wks) return { + sln2005.sharedProjects, sln2005.configurationPlatforms, sln2005.properties, sln2005.nestedProjects, diff --git a/modules/vstudio/vs2010.lua b/modules/vstudio/vs2010.lua index 74f7e167..ed4fcfe7 100644 --- a/modules/vstudio/vs2010.lua +++ b/modules/vstudio/vs2010.lua @@ -69,19 +69,31 @@ end elseif p.project.isc(prj) or p.project.iscpp(prj) then - local projFileModified = p.generate(prj, ".vcxproj", vstudio.vc2010.generate) + if prj.kind == p.SHAREDITEMS then + local projFileModified = p.generate(prj, ".vcxitems", vstudio.vc2013.generate) - -- 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 + if p.generate(prj, ".vcxitems.filters", vstudio.vc2010.generateFilters) == true and projFileModified == false then + -- vs workaround for issue where if only the .filters file is modified, VS doesn't automaticly trigger a reload + p.touch(prj, ".vcxitems") + end + end + else + local projFileModified = p.generate(prj, ".vcxproj", vstudio.vc2010.generate) - -- Only generate a filters file if the source tree actually has subfolders - if tree.hasbranches(project.getsourcetree(prj)) then - if p.generate(prj, ".vcxproj.filters", vstudio.vc2010.generateFilters) == true and projFileModified == false then - -- vs workaround for issue where if only the .filters file is modified, VS doesn't automaticly trigger a reload - p.touch(prj, ".vcxproj") + -- 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 + if p.generate(prj, ".vcxproj.filters", vstudio.vc2010.generateFilters) == true and projFileModified == false then + -- vs workaround for issue where if only the .filters file is modified, VS doesn't automaticly trigger a reload + p.touch(prj, ".vcxproj") + end end end end diff --git a/modules/vstudio/vs2010_vcxproj.lua b/modules/vstudio/vs2010_vcxproj.lua index 8493e221..4109130f 100644 --- a/modules/vstudio/vs2010_vcxproj.lua +++ b/modules/vstudio/vs2010_vcxproj.lua @@ -1210,6 +1210,12 @@ end) local rel = path.translate(file.relpath) + + -- SharedItems projects paths are prefixed with a magical variable + if prj.kind == p.SHAREDITEMS then + rel = "$(MSBuildThisFileDirectory)" .. rel + end + if #contents > 0 then p.push('<%s Include="%s">', tag, rel) p.outln(contents) @@ -1311,14 +1317,39 @@ function m.projectReferences(prj) local refs = project.getdependencies(prj, 'linkOnly') - if #refs > 0 then - p.push('') + -- Handle linked shared items projects + local contents = p.capture(function() + p.push() for _, ref in ipairs(refs) do - local relpath = vstudio.path(prj, vstudio.projectfile(ref)) - p.push('', relpath) - p.callArray(m.elements.projectReferences, prj, ref) - p.pop('') + if ref.kind == p.SHAREDITEMS then + local relpath = vstudio.path(prj, vstudio.projectfile(ref)) + p.x('', relpath) + end end + p.pop() + end) + if #contents > 0 then + p.push('') + p.outln(contents) + p.pop('') + end + + -- Handle all other linked projects + local contents = p.capture(function() + p.push() + for _, ref in ipairs(refs) do + if ref.kind ~= p.SHAREDITEMS then + local relpath = vstudio.path(prj, vstudio.projectfile(ref)) + p.push('', relpath) + p.callArray(m.elements.projectReferences, prj, ref) + p.pop('') + end + end + p.pop() + end) + if #contents > 0 then + p.push('') + p.outln(contents) p.pop('') end end diff --git a/modules/vstudio/vs2010_vcxproj_filters.lua b/modules/vstudio/vs2010_vcxproj_filters.lua index 81cbe80e..dc8b2830 100644 --- a/modules/vstudio/vs2010_vcxproj_filters.lua +++ b/modules/vstudio/vs2010_vcxproj_filters.lua @@ -78,12 +78,19 @@ if files and #files > 0 then p.push('') for _, file in ipairs(files) do + local rel = path.translate(file.relpath) + + -- SharedItems projects paths are prefixed with a magical variable + if prj.kind == p.SHAREDITEMS then + rel = "$(MSBuildThisFileDirectory)" .. rel + end + if file.parent.path then - p.push('<%s Include=\"%s\">', tag, path.translate(file.relpath)) + p.push('<%s Include=\"%s\">', tag, rel) p.w('%s', path.translate(file.parent.path, '\\')) p.pop('', tag) else - p.w('<%s Include=\"%s\" />', tag, path.translate(file.relpath)) + p.w('<%s Include=\"%s\" />', tag, rel) end end p.pop('') diff --git a/modules/vstudio/vs2013.lua b/modules/vstudio/vs2013.lua index fe2887f8..ff9f70e8 100644 --- a/modules/vstudio/vs2013.lua +++ b/modules/vstudio/vs2013.lua @@ -25,7 +25,7 @@ -- The capabilities of this action - valid_kinds = { "ConsoleApp", "WindowedApp", "StaticLib", "SharedLib", "Makefile", "None", "Utility" }, + valid_kinds = { "ConsoleApp", "WindowedApp", "StaticLib", "SharedLib", "Makefile", "None", "Utility", "SharedItems" }, valid_languages = { "C", "C++", "C#", "F#" }, valid_tools = { cc = { "msc" }, diff --git a/modules/vstudio/vs2013_vcxitems.lua b/modules/vstudio/vs2013_vcxitems.lua new file mode 100644 index 00000000..0499f42f --- /dev/null +++ b/modules/vstudio/vs2013_vcxitems.lua @@ -0,0 +1,142 @@ +-- +-- vs2013_vcxitems.lua +-- Generate a Visual Studio 201x C/C++ shared items project. +-- Copyright (c) Jason Perkins and the Premake project +-- + + local p = premake + p.vstudio.vc2013 = {} + + local vstudio = p.vstudio + local project = p.project + local config = p.config + local fileconfig = p.fileconfig + local tree = p.tree + + local m = p.vstudio.vc2013 + local vc2010 = p.vstudio.vc2010 + +--- +-- Add namespace for element definition lists for p.callArray() +--- + + m.elements = {} + m.conditionalElements = {} + +-- +-- Generate a Visual Studio 201x C++ project, with support for the new platforms API. +-- + + m.elements.project = function(prj) + return { + vc2010.xmlDeclaration, + m.project, + m.globals, + m.itemDefinitionGroup, + m.itemGroup, + vc2010.files, + } + end + + function m.generate(prj) + p.utf8() + p.callArray(m.elements.project, prj) + p.out('') + end + +-- +-- Output the XML declaration and opening tag. +-- + + function m.project(prj) + p.push('') + end + +-- +-- Write out the Globals property group. +-- + + m.elements.globals = function(prj) + return { + m.msbuildAllProjects, + m.hasSharedItems, + m.itemsProjectGuid, + m.itemsProjectName, + } + end + + function m.globals(prj) + vc2010.propertyGroup(nil, "Globals") + p.callArray(m.elements.globals, prj) + p.pop('') + end + + function m.msbuildAllProjects(prj) + vc2010.element("MSBuildAllProjects", nil, "$(MSBuildAllProjects);$(MSBuildThisFileFullPath)") + end + + function m.hasSharedItems(prj) + vc2010.element("HasSharedItems", nil, "true") + end + + function m.itemsProjectGuid(prj) + vc2010.element("ItemsProjectGuid", nil, "{%s}", prj.uuid) + end + + function m.itemsProjectName(prj) + if prj.name ~= prj.filename then + vc2010.element("ItemsProjectName", nil, "%s", prj.name) + end + end + +-- +-- Write an item definition group, which contains all of the shared compile settings. +-- + + m.elements.itemDefinitionGroup = function(prj) + return { + m.clCompile, + } + end + + function m.itemDefinitionGroup(prj) + p.push('') + p.callArray(m.elements.itemDefinitionGroup, prj) + p.pop('') + end + + m.elements.clCompile = function(prj) + return { + m.additionalIncludeDirectories, + } + end + + function m.clCompile(prj) + p.push('') + p.callArray(m.elements.clCompile, prj) + p.pop('') + end + + function m.additionalIncludeDirectories(prj) + vc2010.element("AdditionalIncludeDirectories", nil, '%s', '%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)') + end + +-- +-- Write an item group, which contains the project capability +-- + + m.elements.itemGroup = function(prj) + return { + m.projectCapability, + } + end + + function m.itemGroup(prj) + p.push('') + p.callArray(m.elements.itemGroup, prj) + p.pop('') + end + + function m.projectCapability(prj) + p.w('') + end diff --git a/modules/vstudio/vs2015.lua b/modules/vstudio/vs2015.lua index fc9c8536..33a15fea 100644 --- a/modules/vstudio/vs2015.lua +++ b/modules/vstudio/vs2015.lua @@ -25,7 +25,7 @@ -- The capabilities of this action - valid_kinds = { "ConsoleApp", "WindowedApp", "StaticLib", "SharedLib", "Makefile", "None", "Utility" }, + valid_kinds = { "ConsoleApp", "WindowedApp", "StaticLib", "SharedLib", "Makefile", "None", "Utility", "SharedItems" }, valid_languages = { "C", "C++", "C#", "F#" }, valid_tools = { cc = { "msc" }, diff --git a/modules/vstudio/vs2017.lua b/modules/vstudio/vs2017.lua index 1072adf0..1115be8b 100644 --- a/modules/vstudio/vs2017.lua +++ b/modules/vstudio/vs2017.lua @@ -25,7 +25,7 @@ -- The capabilities of this action - valid_kinds = { "ConsoleApp", "WindowedApp", "StaticLib", "SharedLib", "Makefile", "None", "Utility" }, + valid_kinds = { "ConsoleApp", "WindowedApp", "StaticLib", "SharedLib", "Makefile", "None", "Utility", "SharedItems" }, valid_languages = { "C", "C++", "C#", "F#" }, valid_tools = { cc = { "msc" }, diff --git a/modules/vstudio/vs2019.lua b/modules/vstudio/vs2019.lua index 9b361d74..0ae85df5 100644 --- a/modules/vstudio/vs2019.lua +++ b/modules/vstudio/vs2019.lua @@ -25,7 +25,7 @@ -- The capabilities of this action - valid_kinds = { "ConsoleApp", "WindowedApp", "StaticLib", "SharedLib", "Makefile", "None", "Utility" }, + valid_kinds = { "ConsoleApp", "WindowedApp", "StaticLib", "SharedLib", "Makefile", "None", "Utility", "SharedItems" }, valid_languages = { "C", "C++", "C#", "F#" }, valid_tools = { cc = { "msc" }, diff --git a/modules/vstudio/vstudio.lua b/modules/vstudio/vstudio.lua index 83417c9d..55b6cd39 100644 --- a/modules/vstudio/vstudio.lua +++ b/modules/vstudio/vstudio.lua @@ -447,7 +447,11 @@ elseif project.isfsharp(prj) then extension = ".fsproj" elseif project.isc(prj) or project.iscpp(prj) then - extension = iif(_ACTION > "vs2008", ".vcxproj", ".vcproj") + if prj.kind == p.SHAREDITEMS then + extension = ".vcxitems" + else + extension = iif(_ACTION > "vs2008", ".vcxproj", ".vcproj") + end end return p.filename(prj, extension) @@ -644,5 +648,6 @@ include("vs2010_rules_props.lua") include("vs2010_rules_targets.lua") include("vs2010_rules_xml.lua") + include("vs2013_vcxitems.lua") return p.modules.vstudio diff --git a/src/_premake_init.lua b/src/_premake_init.lua index ebc6445e..38b83f0b 100644 --- a/src/_premake_init.lua +++ b/src/_premake_init.lua @@ -701,6 +701,7 @@ "StaticLib", "WindowedApp", "Utility", + "SharedItems", }, } diff --git a/src/base/_foundation.lua b/src/base/_foundation.lua index eeec795e..7be869eb 100644 --- a/src/base/_foundation.lua +++ b/src/base/_foundation.lua @@ -46,6 +46,7 @@ premake.OFF = "Off" premake.POSIX = "posix" premake.PS3 = "ps3" + premake.SHAREDITEMS = "SharedItems" premake.SHAREDLIB = "SharedLib" premake.STATICLIB = "StaticLib" premake.UNICODE = "Unicode"