Skip to content

Commit 796c6e2

Browse files
committed
Support for NuGet source mapping.
1 parent 8fd6254 commit 796c6e2

File tree

9 files changed

+211
-25
lines changed

9 files changed

+211
-25
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ eng/tools
1111
.teamcity/target
1212
/.config/dotnet-tools.json
1313
/.tools
14+
nuget.config

src/PostSharp.Engineering.BuildTools/Build/Model/BuildInfo.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright (c) SharpCrafters s.r.o. See the LICENSE.md file in the root directory of this repository root for details.
22

3+
using PostSharp.Engineering.BuildTools.Dependencies.Model;
34
using System;
45

56
namespace PostSharp.Engineering.BuildTools.Build.Model
@@ -15,9 +16,15 @@ namespace PostSharp.Engineering.BuildTools.Build.Model
1516
public record BuildInfo( string? PackageVersion, string Configuration, string MSBuildConfiguration, string? PackagePreviewVersion )
1617
{
1718
public BuildInfo( string? packageVersion, BuildConfiguration configuration, Product product, string? packagePreviewVersion ) : this(
19+
packageVersion,
20+
configuration,
21+
product.DependencyDefinition,
22+
packagePreviewVersion ) { }
23+
24+
public BuildInfo( string? packageVersion, BuildConfiguration configuration, DependencyDefinition dependencyDefinition, string? packagePreviewVersion ) : this(
1825
packageVersion,
1926
configuration.ToString(),
20-
product.DependencyDefinition.MSBuildConfiguration[configuration],
27+
dependencyDefinition.MSBuildConfiguration[configuration],
2128
packagePreviewVersion ) { }
2229

2330
public bool IsPrerelease => this.PackageVersion?.Contains( "-", StringComparison.Ordinal ) ?? throw new InvalidOperationException();

src/PostSharp.Engineering.BuildTools/Build/Model/Product.cs

Lines changed: 149 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,10 @@ public ParametrizedDependency[] ParametrizedDependencies
210210
internal bool UseDocker => this.ResolvedBuildAgentRequirements.IsDockerHost;
211211

212212
internal DockerSpec? DockerSpec
213-
=> this.ResolvedBuildAgentRequirements.IsDockerHost ? new DockerSpec( $"{this.ProductNameWithoutDot}-{this.ProductFamily.Version}".ToLowerInvariant() ) : null;
214-
213+
=> this.ResolvedBuildAgentRequirements.IsDockerHost
214+
? new DockerSpec( $"{this.ProductNameWithoutDot}-{this.ProductFamily.Version}".ToLowerInvariant() )
215+
: null;
216+
215217
public bool IsPublishingNonReleaseBranchesAllowed { get; init; }
216218

217219
/// <summary>
@@ -236,6 +238,13 @@ internal DockerSpec? DockerSpec
236238
/// </summary>
237239
public Pattern ConsumableDepsFiles { get; init; } = Pattern.Empty;
238240

241+
/// <summary>
242+
/// Gets or sets a value indicating whether the <c>prepare</c> should generate the <c>nuget.config</c> file.
243+
/// </summary>
244+
public bool GenerateNuGetConfig { get; init; }
245+
246+
public string[] OwnPackagePatterns { get; init; } = [];
247+
239248
public bool TryGetDependency( string name, [NotNullWhen( true )] out ParametrizedDependency? dependency )
240249
{
241250
dependency = this.ParametrizedDependencies.SingleOrDefault( d => d.Name == name );
@@ -625,7 +634,7 @@ It must be imported by other repos as a dependency.
625634
private BuildInfo ReadGeneratedVersionFile( string path )
626635
{
627636
var versionFilePath = path;
628-
var versionFile = Project.FromFile( versionFilePath, new ProjectOptions() );
637+
var versionFile = Project.FromFile( versionFilePath, MSBuildLoadOptions.IgnoreImportErrors );
629638

630639
string packageVersion;
631640

@@ -716,7 +725,7 @@ public bool TryReadMainVersionFile(
716725
return false;
717726
}
718727

719-
var versionFile = Project.FromFile( mainVersionFilePath, new ProjectOptions() );
728+
var versionFile = Project.FromFile( mainVersionFilePath, MSBuildLoadOptions.IgnoreImportErrors );
720729

721730
var mainVersion = versionFile
722731
.Properties
@@ -975,7 +984,7 @@ public string GetConfigurationSpecificVersionsFilePath( BuildContext context, Co
975984
return null;
976985
}
977986

978-
var versionFile = Project.FromFile( path, new ProjectOptions() );
987+
var versionFile = Project.FromFile( path, MSBuildLoadOptions.IgnoreImportErrors );
979988

980989
var configuration = versionFile.Properties.SingleOrDefault( p => p.Name == "EngineeringConfiguration" )
981990
?.UnevaluatedValue;
@@ -1147,12 +1156,12 @@ public bool PrepareVersionsFile(
11471156
{
11481157
var configuration = settings.BuildConfiguration;
11491158

1150-
context.Console.WriteMessage( "Preparing the version file" );
1159+
context.Console.WriteMessage( "Preparing the version file." );
11511160

11521161
var propsFilePath = this.GetVersionPropertiesFilePath( context, settings );
11531162
Directory.CreateDirectory( Path.GetDirectoryName( propsFilePath )! );
11541163

1155-
// Load Versions.g.props.
1164+
// Load Versions.<Configuration>.g.props.
11561165
if ( !DependenciesOverrideFile.TryLoad( context, settings, configuration, out dependenciesOverrideFile ) )
11571166
{
11581167
return false;
@@ -1199,6 +1208,12 @@ public bool PrepareVersionsFile(
11991208
return false;
12001209
}
12011210

1211+
// Generate nuget.config.
1212+
if ( this.GenerateNuGetConfig && !TryGenerateNuGetConfig( context, dependenciesOverrideFile, settings ) )
1213+
{
1214+
return false;
1215+
}
1216+
12021217
context.Console.WriteMessage( $"Writing '{propsFilePath}'." );
12031218
File.WriteAllText( propsFilePath, props );
12041219

@@ -1263,7 +1278,7 @@ private bool TryComputeVersion(
12631278
}
12641279
else
12651280
{
1266-
var versionFile = Project.FromFile( dependencySource.VersionFile, new ProjectOptions() );
1281+
var versionFile = Project.FromFile( dependencySource.VersionFile, MSBuildLoadOptions.IgnoreImportErrors );
12671282

12681283
var propertyName = this.MainVersionDependency!.NameWithoutDot + "MainVersion";
12691284

@@ -1375,6 +1390,129 @@ private bool TryComputeVersion(
13751390
return true;
13761391
}
13771392

1393+
private static bool TryGenerateNuGetConfig( BuildContext context, DependenciesOverrideFile dependenciesOverrideFile, BuildSettings buildSettings )
1394+
{
1395+
var product = context.Product;
1396+
var baseFilePath = Path.Combine( context.RepoDirectory, "nuget.base.config" );
1397+
var targetFilePath = Path.Combine( context.RepoDirectory, "nuget.config" );
1398+
1399+
context.Console.WriteMessage( $"Writing '{targetFilePath}'." );
1400+
1401+
XDocument document;
1402+
XElement rootElement;
1403+
1404+
if ( File.Exists( baseFilePath ) )
1405+
{
1406+
document = XDocument.Load( baseFilePath );
1407+
rootElement = document.Root!;
1408+
}
1409+
else
1410+
{
1411+
document = new XDocument();
1412+
rootElement = new XElement( "configuration" );
1413+
document.Add( rootElement );
1414+
}
1415+
1416+
var packageSourceMappingElement = rootElement.Element( "packageSourceMapping" );
1417+
1418+
if ( packageSourceMappingElement == null )
1419+
{
1420+
packageSourceMappingElement = new XElement( "packageSourceMapping" );
1421+
rootElement.Add( packageSourceMappingElement );
1422+
}
1423+
1424+
var packageSourcesElement = rootElement.Element( "packageSources" );
1425+
1426+
if ( packageSourcesElement == null )
1427+
{
1428+
packageSourcesElement = new XElement( "packageSources" );
1429+
rootElement.Add( packageSourcesElement );
1430+
}
1431+
1432+
// Add the current artifact directory.
1433+
var artifactDirectory = Path.Combine(
1434+
context.RepoDirectory,
1435+
product.PrivateArtifactsDirectory.ToString( new BuildInfo( null, buildSettings.BuildConfiguration, product, null ) ) );
1436+
1437+
AddDirectory( product.ProductName, artifactDirectory, product.DependencyDefinition.PackagePatterns );
1438+
1439+
// Add dependencies.
1440+
foreach ( var dependency in dependenciesOverrideFile.Dependencies )
1441+
{
1442+
var dependencyDefinition = product.GetDependencyDefinition( dependency.Key );
1443+
var parametrizedDependency = product.ParametrizedDependencies.Single( d => d.Name == dependency.Key );
1444+
var dependencyDirectory = Path.GetDirectoryName( dependency.Value.VersionFile )!;
1445+
1446+
if ( dependency.Value.SourceKind == DependencySourceKind.Local )
1447+
{
1448+
dependencyDirectory = Path.Combine(
1449+
dependencyDirectory,
1450+
dependencyDefinition.PrivateArtifactsDirectory.ToString(
1451+
new BuildInfo(
1452+
null,
1453+
parametrizedDependency.ConfigurationMapping[buildSettings.BuildConfiguration],
1454+
dependencyDefinition,
1455+
null ) ) );
1456+
}
1457+
1458+
if ( !AddDirectory( dependency.Key, dependencyDirectory, dependencyDefinition.PackagePatterns ) )
1459+
{
1460+
return false;
1461+
}
1462+
}
1463+
1464+
document.Save( targetFilePath );
1465+
1466+
return true;
1467+
1468+
bool AddDirectory( string name, string directory, string[]? patterns )
1469+
{
1470+
var addElement = new XElement( "add" );
1471+
addElement.Add( new XAttribute( "key", name ) );
1472+
addElement.Add( new XAttribute( "value", directory ) );
1473+
packageSourcesElement.Add( addElement );
1474+
1475+
var packageSourceElement = new XElement( "packageSource" );
1476+
packageSourceElement.Add( new XAttribute( "key", name ) );
1477+
packageSourceMappingElement.Add( packageSourceElement );
1478+
1479+
foreach ( var pattern in patterns ?? [] )
1480+
{
1481+
AddPattern( pattern );
1482+
}
1483+
1484+
return true;
1485+
1486+
void AddPattern( string pattern )
1487+
{
1488+
var packageElement = new XElement( "package" );
1489+
packageElement.Add( new XAttribute( "pattern", pattern ) );
1490+
packageSourceElement.Add( packageElement );
1491+
}
1492+
}
1493+
}
1494+
1495+
private static string? ParsePackageName( string packageFile )
1496+
{
1497+
var fileName = Path.GetFileNameWithoutExtension( packageFile );
1498+
1499+
// Use NuGet.Versioning to properly extract package name without version suffix
1500+
var packageNameParts = fileName.Split( '-' )[0].Split( '.' );
1501+
string? packageName = null;
1502+
1503+
for ( var i = packageNameParts.Length - 1; i > 0; i-- )
1504+
{
1505+
if ( !int.TryParse( packageNameParts[i], out _ ) )
1506+
{
1507+
packageName = string.Join( ".", packageNameParts.Take( i + 1 ) );
1508+
1509+
break;
1510+
}
1511+
}
1512+
1513+
return packageName;
1514+
}
1515+
13781516
private bool TryGenerateManifestFileContent(
13791517
VersionComponents version,
13801518
BuildConfiguration configuration,
@@ -1518,7 +1656,7 @@ void AppendToArcadeSuffix( string s )
15181656
foreach ( var kvp in this.ExportedProperties )
15191657
{
15201658
var propsFilePath = Path.Combine( context.RepoDirectory, kvp.Key );
1521-
var propsFile = Project.FromFile( propsFilePath, new ProjectOptions() );
1659+
var propsFile = Project.FromFile( propsFilePath, MSBuildLoadOptions.IgnoreImportErrors );
15221660

15231661
foreach ( var exportedPropertyName in kvp.Value )
15241662
{
@@ -2330,7 +2468,7 @@ private bool TryReadDependencyVersionsFromSourceRepos(
23302468
}
23312469

23322470
var document = XDocument.Parse( mainVersionContent );
2333-
var project = Project.FromXmlReader( document.CreateReader(), new ProjectOptions() );
2471+
var project = Project.FromXmlReader( document.CreateReader(), MSBuildLoadOptions.IgnoreImportErrors );
23342472
var mainVersionPropertyValue = project.Properties.FirstOrDefault( p => p.Name == "MainVersion" )?.EvaluatedValue;
23352473

23362474
if ( string.IsNullOrEmpty( mainVersionPropertyValue ) )
@@ -2682,7 +2820,7 @@ private static BuildConfiguration GetDependencyConfiguration( DependencyDefiniti
26822820
throw new InvalidOperationException( $"The dependency '{definition.Name}' is not resolved. " );
26832821
}
26842822

2685-
var project = Project.FromFile( source.VersionFile, new ProjectOptions() );
2823+
var project = Project.FromFile( source.VersionFile, MSBuildLoadOptions.IgnoreImportErrors );
26862824
var property = project.AllEvaluatedProperties.Single( p => p.Name == $"{definition.NameWithoutDot}BuildConfiguration" );
26872825

26882826
ProjectCollection.GlobalProjectCollection.UnloadAllProjects();

src/PostSharp.Engineering.BuildTools/Dependencies/Definitions/MetalamaDependencies.V2026_0.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public MetalamaDependencyDefinition(
5656
public static ProductFamily Family { get; } = new( _projectName, "2026.0", DevelopmentDependencies.Family, PostSharpDependencies.V2026_0.Family )
5757
{
5858
UpstreamProductFamily = V2025_2.Family
59+
5960
// DownstreamProductFamily = V2026_1.Family
6061
};
6162

@@ -85,6 +86,16 @@ public MetalamaDependencyDefinition(
8586
VcsProvider.GitHub,
8687
MetalamaGitHubOrganization.Metalama )
8788
{
89+
PackagePatterns =
90+
[
91+
"Metalama.Backstage*",
92+
"Metalama.Framework*",
93+
"Metalama.Extensions*",
94+
"Metalama.Patterns*",
95+
"Metalama.LinqPad*",
96+
"Metalama.Migration*"
97+
]
98+
8899
// SuppressUpstream = true
89100
};
90101

src/PostSharp.Engineering.BuildTools/Dependencies/Definitions/PostSharpDependencies.V2026_0.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ public PostSharpDependencyDefinition()
3939
false )
4040
{
4141
this.EngineeringDirectory = @"Build\Distribution\eng";
42+
43+
// We intentionally exclude PostSharp.Engineering.Sdk because it is referenced from global.json
44+
// which is not often updated referencing PostSharp.Engineering locally, so exact the package version
45+
// is not present in the local artifact directory.
46+
this.PackagePatterns = ["PostSharp.Engineering.BuildTools.*"];
4247
}
4348
}
4449

src/PostSharp.Engineering.BuildTools/Dependencies/DependenciesHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ private static bool TryGetTransitiveDependencies(
124124
continue;
125125
}
126126

127-
var versionFile = Project.FromFile( directDependency.Source.VersionFile!, new ProjectOptions() );
127+
var versionFile = Project.FromFile( directDependency.Source.VersionFile!, MSBuildLoadOptions.IgnoreImportErrors );
128128

129129
var transitiveDependencies = versionFile.Items.Where( i => i.ItemType == directDependency.Dependency.NameWithoutDot + "Dependencies" );
130130

src/PostSharp.Engineering.BuildTools/Dependencies/Model/DependencyDefinition.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ namespace PostSharp.Engineering.BuildTools.Dependencies.Model
1717
[PublicAPI]
1818
public class DependencyDefinition
1919
{
20+
private readonly string[]? _packagePatterns;
21+
2022
[return: NotNullIfNotNull( "dependency" )]
2123
public static implicit operator DependencyDefinition?( ParametrizedDependency? dependency ) => dependency?.Definition;
2224

@@ -63,6 +65,12 @@ public class DependencyDefinition
6365

6466
public ParametricString PublicArtifactsDirectory { get; init; } = Path.Combine( "artifacts", "publish", "public" );
6567

68+
public string[] PackagePatterns
69+
{
70+
get => this._packagePatterns ?? [this.Name, this.Name + "*"];
71+
init => this._packagePatterns = value;
72+
}
73+
6674
/// <summary>
6775
/// Gets or sets the order in which products in the same family should be built. This is a poorman version of a recursive build
6876
/// taking dependencies into account, because PostSharp.Engineering does not know detailed dependencies.

0 commit comments

Comments
 (0)