Fix NuGet assembly references in C# projects

For C# projects, we need to get the assembly paths from the NuGet API,
instead of assuming that the package only contains one assembly with the
same name as the package.
This commit is contained in:
Aleksi Juvani 2017-04-07 17:07:17 +03:00
parent bb992e6576
commit c57477229c
3 changed files with 103 additions and 67 deletions

View File

@ -12,23 +12,6 @@
local config = p.config
--
-- All valid .NET Framework versions, from oldest to newest.
--
vstudio.frameworkVersions =
{
"1.0",
"1.1",
"2.0",
"3.0",
"3.5",
"4.0",
"4.5",
"4.6",
}
--
-- Mapping tables from Premake systems and architectures to Visual Studio
-- identifiers. Broken out as tables so new values can be pushed in by

View File

@ -368,35 +368,89 @@
function cs2005.nuGetReferences(prj)
if _ACTION >= "vs2010" then
for i = 1, #prj.nuget do
local package = prj.nuget[i]
_x(2, '<Reference Include="%s">', vstudio.nuget2010.packageId(package))
for _, package in ipairs(prj.nuget) do
local id = vstudio.nuget2010.packageId(package)
local packageAPIInfo = vstudio.nuget2010.packageAPIInfo(package)
-- We need to write HintPaths for all supported framework
-- versions. The last HintPath will override any previous
-- HintPaths (if the condition is met that is).
local cfg = p.project.getfirstconfig(prj)
local action = premake.action.current()
local targetFramework = cfg.dotnetframework or action.vstudio.targetFramework
for _, frameworkVersion in ipairs(cs2005.identifyFrameworkVersions(prj)) do
local packageAPIInfo = vstudio.nuget2010.packageAPIInfo(package)
local assembly = vstudio.path(
prj,
p.filename(
prj.solution,
string.format(
"packages\\%s.%s\\lib\\%s\\%s.dll",
vstudio.nuget2010.packageId(package),
packageAPIInfo.verbatimVersion or packageAPIInfo.version,
cs2005.formatNuGetFrameworkVersion(frameworkVersion),
vstudio.nuget2010.packageId(package)
)
)
)
-- This is a bit janky. To compare versions, we extract all
-- numbers from the given string and right-pad the result with
-- zeros. Then we can just do a lexicographical compare on the
-- resulting strings.
--
-- This is so that we can compare version strings such as
-- "4.6" and "net451" with each other.
_x(3, '<HintPath Condition="Exists(\'%s\')">%s</HintPath>', assembly, assembly)
local function makeVersionComparable(a)
local numbers = ""
for number in a:gmatch("%d") do
numbers = numbers .. number
end
return string.format("%-10d", numbers):gsub(" ", "0")
end
_p(3, '<Private>True</Private>')
_p(2, '</Reference>')
local targetVersion = makeVersionComparable(targetFramework)
-- Figure out what folder contains the files for the nearest
-- supported .NET Framework version.
local files = {}
local bestVersion, bestFolder
for _, file in ipairs(packageAPIInfo.packageEntries) do
-- If this exporter ever supports frameworks such as
-- "netstandard1.3", "sl4", "sl5", "uap10", "wp8" or
-- "wp71", this code will need changing to match the right
-- folders.
local folder = file:match("^lib\\net(%d+)\\")
if folder and path.hasextension(file, ".dll") then
files[folder] = files[folder] or {}
table.insert(files[folder], file)
local version = makeVersionComparable(file:match("lib\\net(%d+)\\"))
if version <= targetVersion and (not bestVersion or version > bestVersion) then
bestVersion = version
bestFolder = folder
end
end
end
if not bestVersion then
p.error("NuGet package '%s' is not compatible with project '%s' .NET Framework version '%s'", id, prj.name, targetFramework)
end
-- Now, add references for all DLLs in that folder.
for _, file in ipairs(files[bestFolder]) do
-- There's some stuff missing from this include that we
-- can't get from the API and would need to download and
-- extract the package to figure out. It looks like we can
-- just omit it though.
--
-- So, for example, instead of:
--
-- <Reference Include="nunit.framework, Version=3.6.1.0,
-- <Culture=neutral, PublicKeyToken=2638cd05610744eb,
-- <processorArchitecture=MSIL">
--
-- We're just outputting:
--
-- <Reference Include="nunit.framework">
_x(2, '<Reference Include="%s">', path.getbasename(file))
_x(3, '<HintPath>%s</HintPath>', vstudio.path(prj, p.filename(prj.solution, string.format("packages\\%s.%s\\%s", id, packageAPIInfo.verbatimVersion or packageAPIInfo.version, file))))
_p(3, '<Private>True</Private>')
_p(2, '</Reference>')
end
end
end
end
@ -487,32 +541,6 @@
end
--
-- Build and return a list of all .NET Framework versions up to and including
-- the project's framework version.
--
function cs2005.identifyFrameworkVersions(prj)
local frameworks = {}
local cfg = p.project.getfirstconfig(prj)
local action = premake.action.current()
local targetFramework = cfg.dotnetframework or action.vstudio.targetFramework
for _, frameworkVersion in ipairs(vstudio.frameworkVersions) do
if frameworkVersion == targetFramework then
break
end
table.insert(frameworks, frameworkVersion)
end
table.insert(frameworks, targetFramework)
return frameworks
end
--
-- When given a .NET Framework version, returns it formatted for NuGet.
--

View File

@ -140,6 +140,31 @@
packageAPIInfo.verbatimVersion = response.verbatimVersion
packageAPIInfo.version = response.version
-- C++ packages don't have this, but C# packages have a
-- packageEntries field that lists all the files in the
-- package. We need to look at this to figure out what
-- DLLs to reference in the project file.
if prj.language == "C#" and not response.packageEntries then
p.error("NuGet package '%s' has no file listing (are you sure referenced a .NET package and not a native package?)", id)
end
if prj.language == "C#" then
packageAPIInfo.packageEntries = {}
for _, item in ipairs(response.packageEntries) do
if not item.fullName then
p.error("Failed to understand NuGet API response (package '%s' version '%s' packageEntry has no fullName)", id, version)
end
table.insert(packageAPIInfo.packageEntries, path.translate(item.fullName))
end
if #packageAPIInfo.packageEntries == 0 then
p.error("NuGet package '%s' file listing is empty", id)
end
end
break
end
end