Skip to content

Commit 2f407eb

Browse files
committed
[Library Ordering] Automatically sort libraries in their dependency order, then apply user order to resolve any conflicts at the same dependency level.
1 parent ccca5aa commit 2f407eb

File tree

3 files changed

+153
-17
lines changed

3 files changed

+153
-17
lines changed

Sharpmake.UnitTests/DependencyPropagationTest.cs

+57-1
Original file line numberDiff line numberDiff line change
@@ -1086,17 +1086,58 @@ public void LibInheritLibAndDLLPrivate()
10861086
Assert.True(conf.DependenciesLibraryFiles.ContainsElement("LibDependOnDLLProject"));
10871087
}
10881088
}
1089+
1090+
[Test]
1091+
public void AutoDependencyOrder()
1092+
{
1093+
var project = GetProject<SharpmakeProjects.ProjectDependencyOrder>();
1094+
Assert.IsNotNull(project);
1095+
Assert.IsNotNull(project);
1096+
1097+
foreach (var conf in project.Configurations)
1098+
{
1099+
Assert.That(conf.ResolvedDependencies.Count, Is.EqualTo(4));
1100+
1101+
Assert.True(conf.ResolvedDependencies.ContainsProjectType(typeof(SharpmakeProjects.NoDependencyProject1)));
1102+
Assert.True(conf.ResolvedDependencies.ContainsProjectType(typeof(SharpmakeProjects.NoDependencyProject2)));
1103+
Assert.True(conf.ResolvedDependencies.ContainsProjectType(typeof(SharpmakeProjects.OnePrivateDependencyProject)));
1104+
Assert.True(conf.ResolvedDependencies.ContainsProjectType(typeof(SharpmakeProjects.TwoPrivateDependenciesProject)));
1105+
1106+
int rootProjectOrder = conf.TargetFileOrderNumber;
1107+
int project1Order = conf.ResolvedDependencies.OfProjectType(typeof(SharpmakeProjects.NoDependencyProject1)).First().TargetFileOrderNumber;
1108+
int project2Order = conf.ResolvedDependencies.OfProjectType(typeof(SharpmakeProjects.NoDependencyProject2)).First().TargetFileOrderNumber;
1109+
int onePrivateOrder = conf.ResolvedDependencies.OfProjectType(typeof(SharpmakeProjects.OnePrivateDependencyProject)).First().TargetFileOrderNumber;
1110+
int twoPrivateOrder = conf.ResolvedDependencies.OfProjectType(typeof(SharpmakeProjects.TwoPrivateDependenciesProject)).First().TargetFileOrderNumber;
1111+
1112+
// I can't know the exact values but dependencies should at least appear as higher values
1113+
Assert.Less(rootProjectOrder, project1Order);
1114+
Assert.Less(rootProjectOrder, project2Order);
1115+
1116+
Assert.Less(onePrivateOrder, project1Order);
1117+
Assert.Less(onePrivateOrder, project2Order);
1118+
1119+
Assert.Less(twoPrivateOrder, project1Order);
1120+
Assert.Less(twoPrivateOrder, project2Order);
1121+
1122+
Assert.AreEqual(onePrivateOrder, twoPrivateOrder);
1123+
Assert.AreEqual(project1Order, project2Order);
1124+
}
1125+
}
10891126
}
10901127

10911128
public static class UTestUtilities
10921129
{
10931130
public static readonly string IncludeFolder = "include";
10941131
public static readonly string LibOutputFolder = "lib_output";
10951132

1096-
public static bool ContainsProjectType(this System.Collections.Generic.IEnumerable<Project.Configuration> dependencies, System.Type projectType)
1133+
public static bool ContainsProjectType(this IEnumerable<Project.Configuration> dependencies, Type projectType)
10971134
{
10981135
return dependencies.Select(x => x.Project.GetType()).Contains(projectType);
10991136
}
1137+
public static IEnumerable<Project.Configuration> OfProjectType(this IEnumerable<Project.Configuration> dependencies, Type projectType)
1138+
{
1139+
return dependencies.Where(x => x.Project.GetType() == projectType);
1140+
}
11001141

11011142
public static bool ContainsProjectType(this IEnumerable<DotNetDependency> dependencies, Type projectType)
11021143
{
@@ -1741,5 +1782,20 @@ public void ConfigureAll(Configuration conf, Target target)
17411782
conf.AddPrivateDependency<OnePrivateDependencyProject>(target);
17421783
}
17431784
}
1785+
1786+
[Sharpmake.Generate]
1787+
public class ProjectDependencyOrder : UTestUtilities.UnitTestCommonProject
1788+
{
1789+
public ProjectDependencyOrder() { }
1790+
1791+
[Configure()]
1792+
public void ConfigureAll(Configuration conf, Target target)
1793+
{
1794+
conf.AddPrivateDependency<OnePrivateDependencyProject>(target);
1795+
conf.AddPrivateDependency<TwoPrivateDependenciesProject>(target);
1796+
conf.AddPrivateDependency<NoDependencyProject1>(target);
1797+
conf.AddPrivateDependency<NoDependencyProject2>(target);
1798+
}
1799+
}
17441800
}
17451801
}

Sharpmake/Project.Configuration.cs

+50-5
Original file line numberDiff line numberDiff line change
@@ -1151,7 +1151,7 @@ public void AddDependencyBuiltTargetLibraryFile(string libraryFile, int orderNum
11511151
{
11521152
if (_linkState != LinkState.Linking)
11531153
throw new Error($"Cannot add built target lib '{libraryFile}' outside of the link process of the Project.Configuration");
1154-
DependenciesBuiltTargetsLibraryFiles.Add(libraryFile, orderNumber);
1154+
DependenciesBuiltTargetsLibraryFiles.Add(libraryFile, orderNumber, OrderableStrings.OrderResolve.Greater);
11551155
}
11561156

11571157
public OrderableStrings DependenciesForceUsingFiles = new OrderableStrings();
@@ -1697,10 +1697,33 @@ internal void Resolve(Resolver resolver)
16971697
public string TargetFileFullNameWithExtension { get; internal set; } = "[conf.TargetFileFullName][conf.TargetFileFullExtension]";
16981698

16991699
/// <summary>
1700-
/// Gets or sets the ordering index of the target when added as a library to another
1700+
/// Sets the ordering index of the target when added as a library to another. This will only affect the order of libraries
1701+
/// at the same dependency level. Dependent libraries always appear first.
17011702
/// project.
1703+
/// Gets the combined user order and dependency order.
17021704
/// </summary>
1703-
public int TargetFileOrderNumber = 0;
1705+
public int TargetFileOrderNumber
1706+
{
1707+
get
1708+
{
1709+
return _targetFileOrderNumber + _autoFileOrderNumer;
1710+
}
1711+
set
1712+
{
1713+
if (value <= -_autoFileNumberStep/2 || value >= _autoFileNumberStep/2)
1714+
{
1715+
throw new ArgumentOutOfRangeException(string.Format("TargetFileOrderNumber should only be between {0} and {1}. Got {2}",
1716+
-_autoFileNumberStep / 2, _autoFileNumberStep / 2, value));
1717+
}
1718+
_targetFileOrderNumber = value;
1719+
}
1720+
}
1721+
1722+
internal int _targetFileOrderNumber = 0;
1723+
// Used internaly to order by dependency before applying TargetFileOrderNumber or other ordering
1724+
internal int _autoFileOrderNumer = 0;
1725+
// Make depdencies 1000 items from each other, this means that a user would need to put huge numbers in target file orders to cause cross over
1726+
internal const int _autoFileNumberStep = 1000;
17041727

17051728
/// <summary>
17061729
/// Gets or sets the ordering index of the library paths when added as a library to
@@ -2856,6 +2879,21 @@ internal DependencyNode(Configuration inConfiguration, DependencySetting inDepen
28562879
_dependencySetting = inDependencySetting;
28572880
}
28582881

2882+
internal void UpdateAutoDependencies()
2883+
{
2884+
// Configurations are shared so it's likely the dependency has been processed already so don't process it again
2885+
// dependency roots will always be 0, but also have no children, anyone else will have a non-zero value once set
2886+
// and that value should remain stable each time the dependency tree is walked.
2887+
if (_configuration._autoFileOrderNumer == 0)
2888+
{
2889+
foreach (var child in _childNodes)
2890+
{
2891+
child.Key.UpdateAutoDependencies();
2892+
_configuration._autoFileOrderNumer = Math.Min(_configuration._autoFileOrderNumer, child.Key._configuration._autoFileOrderNumer - 1000);
2893+
}
2894+
}
2895+
}
2896+
28592897
internal Configuration _configuration;
28602898
internal DependencySetting _dependencySetting;
28612899
internal Dictionary<DependencyNode, DependencyType> _childNodes = new Dictionary<DependencyNode, DependencyType>();
@@ -3105,8 +3143,9 @@ internal void Link(Builder builder)
31053143
if (dependencySetting.HasFlag(DependencySetting.LibraryPaths))
31063144
DependenciesOtherLibraryPaths.AddRange(dependency.LibraryPaths);
31073145

3146+
// Use dependency.TargetFileOrderNumber to make sure to group dependent libraries by their dependencies
31083147
if (dependencySetting.HasFlag(DependencySetting.LibraryFiles))
3109-
DependenciesOtherLibraryFiles.AddRange(dependency.LibraryFiles);
3148+
DependenciesOtherLibraryFiles.AddRange(dependency.LibraryFiles, dependency.TargetFileOrderNumber, OrderableStrings.OrderResolve.Greater);
31103149

31113150
if (dependencySetting.HasFlag(DependencySetting.ForceUsingAssembly))
31123151
DependenciesForceUsingFiles.AddRange(dependency.ForceUsingFiles);
@@ -3138,8 +3177,9 @@ internal void Link(Builder builder)
31383177
if (dependencySetting.HasFlag(DependencySetting.LibraryPaths))
31393178
DependenciesOtherLibraryPaths.AddRange(dependency.LibraryPaths);
31403179

3180+
// Use dependency.TargetFileOrderNumber to make sure to group dependent libraries by their dependencies
31413181
if (dependencySetting.HasFlag(DependencySetting.LibraryFiles))
3142-
DependenciesOtherLibraryFiles.AddRange(dependency.LibraryFiles);
3182+
DependenciesOtherLibraryFiles.AddRange(dependency.LibraryFiles, dependency.TargetFileOrderNumber, OrderableStrings.OrderResolve.Greater);
31433183
}
31443184
}
31453185

@@ -3303,6 +3343,7 @@ static private DependencyNode BuildDependencyNodeTree(Builder builder, Configura
33033343

33043344
Stack<DependencyNode> visiting = new Stack<DependencyNode>();
33053345
visiting.Push(rootNode);
3346+
33063347
while (visiting.Count > 0)
33073348
{
33083349
DependencyNode visitedNode = visiting.Pop();
@@ -3340,6 +3381,7 @@ static private DependencyNode BuildDependencyNodeTree(Builder builder, Configura
33403381
if (!visitedConfiguration._dependenciesSetting.TryGetValue(pair, out dependencySetting))
33413382
dependencySetting = DependencySetting.Default;
33423383

3384+
// We use steps of 1000 to allow for related libraries to be grouped alongside their dependencies
33433385
DependencyNode childNode = new DependencyNode(dependencyConf, dependencySetting);
33443386
System.Diagnostics.Debug.Assert(!visitedNode._childNodes.ContainsKey(childNode));
33453387
visitedNode._childNodes.Add(childNode, dependencyType);
@@ -3349,6 +3391,9 @@ static private DependencyNode BuildDependencyNodeTree(Builder builder, Configura
33493391
}
33503392
}
33513393

3394+
// update dependency hierarchy
3395+
rootNode.UpdateAutoDependencies();
3396+
33523397
return rootNode;
33533398
}
33543399

Sharpmake/Strings.cs

+46-11
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,14 @@ public void Add(string item)
253253
_list.Add(new StringEntry(item));
254254
}
255255

256-
public void Add(string item, int orderNumber)
256+
public enum OrderResolve
257+
{
258+
None,
259+
Less,
260+
Greater
261+
}
262+
263+
public void Add(string item, int orderNumber, OrderResolve resolveMethod = OrderResolve.None)
257264
{
258265
if (_hashSet.Add(item))
259266
_list.Add(new StringEntry(item, orderNumber));
@@ -268,9 +275,22 @@ public void Add(string item, int orderNumber)
268275
_list[i] = new StringEntry(item, orderNumber);
269276
else if (_list[i].OrderNumber != orderNumber)
270277
{
271-
throw new Error(
272-
"Cannot specify 2 different non-zero order number for \"" +
273-
item + "\": " + _list[i].OrderNumber + " and " + orderNumber);
278+
if (resolveMethod == OrderResolve.Less)
279+
{
280+
if (orderNumber < _list[i].OrderNumber)
281+
_list[i] = new StringEntry(item, orderNumber);
282+
}
283+
else if (resolveMethod == OrderResolve.Greater)
284+
{
285+
if (orderNumber > _list[i].OrderNumber)
286+
_list[i] = new StringEntry(item, orderNumber);
287+
}
288+
else
289+
{
290+
throw new Error(
291+
"Cannot specify 2 different non-zero order number for \"" +
292+
item + "\": " + _list[i].OrderNumber + " and " + orderNumber);
293+
}
274294
}
275295
}
276296
}
@@ -283,18 +303,20 @@ public void AddRange(IEnumerable<string> collection)
283303
Add(item);
284304
}
285305

286-
public void AddRange(OrderableStrings collection)
306+
public void AddRange(OrderableStrings collection, int outerOrderNumber = 0, OrderResolve resolveMethod = OrderResolve.None)
287307
{
288308
List<StringEntry> existingEntriesToAdd = null;
289309
foreach (var entry in collection._list)
290310
{
311+
var newEntry = new StringEntry(entry.StringValue, entry.OrderNumber + outerOrderNumber);
312+
291313
if (_hashSet.Add(entry.StringValue))
292-
_list.Add(entry);
293-
else if (entry.OrderNumber != 0) // make sure to have orderNumber
314+
_list.Add(newEntry);
315+
else if (newEntry.OrderNumber != 0) // make sure to have orderNumber
294316
{
295317
if (existingEntriesToAdd == null)
296318
existingEntriesToAdd = new List<StringEntry>();
297-
existingEntriesToAdd.Add(entry);
319+
existingEntriesToAdd.Add(newEntry);
298320
}
299321
}
300322
if (existingEntriesToAdd != null)
@@ -309,9 +331,22 @@ public void AddRange(OrderableStrings collection)
309331
_list[i] = new StringEntry(_list[i].StringValue, orderNumber);
310332
else if (_list[i].OrderNumber != orderNumber)
311333
{
312-
throw new Error(
313-
"Cannot specify 2 different non-zero order number for \"" +
314-
_list[i].StringValue + "\": " + _list[i].OrderNumber + " and " + orderNumber);
334+
if (resolveMethod == OrderResolve.Less)
335+
{
336+
if (orderNumber < _list[i].OrderNumber)
337+
_list[i] = new StringEntry(_list[i].StringValue, orderNumber);
338+
}
339+
else if (resolveMethod == OrderResolve.Greater)
340+
{
341+
if (orderNumber > _list[i].OrderNumber)
342+
_list[i] = new StringEntry(_list[i].StringValue, orderNumber);
343+
}
344+
else
345+
{
346+
throw new Error(
347+
"Cannot specify 2 different non-zero order number for \"" +
348+
_list[i].StringValue + "\": " + _list[i].OrderNumber + " and " + orderNumber);
349+
}
315350
}
316351
}
317352
}

0 commit comments

Comments
 (0)